类加载机制及SPI

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

类加载机制及SPI
最近重温Java类加载及双亲委派机制,并写了⼀个SPI的例⼦
从⽹上找了⼀张图⽚,对着图⽚及课堂笔记来梳理下。

⾸先java⾃带的类加载器分为BootStrapClassLoader(引导\启动类加载器),ExtClassLoader(扩展类加载器),AppClassLoader(应⽤程序类加载器)三种,此外还⽀持⽤户⾃⼰定义的⾃定义类加载器,加载的是⽤户⾃⼰指定的⽬录。

BootStrapClassLoader:jvm中,c++处理类加载的这套逻辑,被称为启动类加载器,是由c++编写的,在java中为null,加载的路径是
Jre/lib/rt.jar, 在这个过程中会通过启动类加载器,来加载uncherHelper,并执⾏checkAndLoadMain,以及加载main函数所在的类,并启动扩展类加载器、应⽤类加载器
ExtClassLoader: 扩展类加载器,加载的是Jre/lib/ext/*.jar,查看⽅式:
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader().getParent();
URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
URL[] urls = urlClassLoader.getURLs();
for (URL url : urls) {
System.out.println(url);
}
}
AppClassLoader: 应⽤类加载器,加载⽤户程序的类加载器,加载的是CLASS_PATH中指定的所有jar
public static void main(String[] args) {
String[] urls = System.getProperty("java.class.path").split(":");
for (String url : urls) {
System.out.println(url);
}
System.out.println("---------------------------------------------------------");
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
URL[] urls1 = classLoader.getURLs();
for (URL url : urls1) {
System.out.println(url);
}
}
双亲委派机制:类加载时,AppClassLoader 会先查看⾃⾝是否已经加载过当前class⽂件,如果加载过则直接返回,如果没有加载过,则委托他的⽗类(ExtClassLoader)尝试进⾏加载,ExtClassLoader也会先查看⾃⼰是否加载过,加载过则直接返回,没有加载过,则继续委派给BootStrapClassLoader,如果直⾄BootStrapClassLoader都没有加载过,则会AppClassLoader会尝试进⾏加载。

打破双亲委派的⽅式:改变这个加载流程,不向上委派
package com.learn;
import .URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CustomClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (name.startsWith("com.learn")) { // 打破双亲委派
c = findClass(name);
} else {
c = this.getParent().loadClass(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return null;
}
}
SPI:⼀种服务发现机制。

它通过在ClassPath路径下的META-INF/services⽂件夹查找⽂件,⾃动加载⽂件⾥所定义的类。

这⼀机制为很多框架扩展提供了可能,⽐如在Dubbo、JDBC中都使⽤到了SPI机制
代码如下:
⾸先定义⼀个公共接⼝
package mon;
public interface IJdbc {
void connection();
}
然后是两个实现类
package com.learn.spi.mysql;
import mon.IJdbc;
public class MysqlJdbc implements IJdbc {
@Override
public void connection() {
System.out.println("this is MysqlJdbc...");
}
}
package com.learn.spi.oracle;
import mon.IJdbc;
public class OracleJdbc implements IJdbc {
@Override
public void connection() {
System.out.println("this is OracleJdbc...");
}
}
最后main函数使⽤ServiceLoader调⽤
package com.learn.spi.gateway;
import mon.IJdbc;
import java.util.ServiceLoader;
public class GateWayMain {
public static void main(String[] args) {
ServiceLoader<IJdbc> iPays = ServiceLoader.load(IJdbc.class);
for (IJdbc iJdbc : iPays) {
iJdbc.connection();
}
System.out.println("end...");
}
}
META-INF/services⽂件夹下的⽂件名定义为接⼝全路径名,⽂件内容为实现类全路径名,这⾥是JDK源码中规定死的com.learn.spi.oracle.OracleJdbc
com.learn.spi.mysql.MysqlJdbc
执⾏结果:。

相关文档
最新文档