public Launcher() {
Launcher.ExtClassLoader var1;
try {
//初始化扩展类加载器,注意这里构造函数没有入参,即无法获取根类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//初始化应用类加载器,注意这里的入参就是扩展类加载器
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//设置上下文类加载器,这个后面会详细说
Thread.currentThread().setContextClassLoader(this.loader);
//删除了一些安全方面的代码
//...
}
//设置上下文类加载器,这个后面会详细说
Thread.currentThread().setContextClassLoader(this.loader);
补充一个代码段:
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
//获取调用者的类加载器
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
//如果为null,则使用上下文类加载器
//这里是重点,什么时候类加载器才会为null? 当然就是由根类加载器加载的类了
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
//...省略
for(DriverInfo aDriver : registeredDrivers) {
//使用上下文类加载器去加载驱动
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
//如果加载成功,则进行连接
Connection con = aDriver.driver.connect(url, info);
//...
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}
//...
}
}
重点说明:
为什么上下文类加载器就可以加载到数据库驱动呢?回到上面一开始Launcher初始化类加载器的源码,我们发现原来所谓的上下文类加载器本质上就是应用类加载器,有没有豁然开朗的感觉?上下文类加载器只是为了解决类的逆向访问提出来的一个概念,并不是一个全新的类加载器,它本质上就是应用类加载器。
链接:https://www.jianshu.com/p/a6ba4f152968
转载:https://blog.csdn.net/zhoudaxia/article/details/35897057
为什么用线程上下文?因为SPI接口一般在核心库里,有引导类加载器(BootStrap)加载,然后具体实现是在系统类加载器(APP)里面,引导类只加载核心库里的,又不能代理给系统类加载器,因为引导类是系统类的祖先
这个更好理解:https://blog.csdn.net/javazejian/article/details/73413292
但是我有个疑问:
可以这样理解吗:
我要新建一个对象,那返回的应该是调用new object()方法时候线程(creator)的加载器,(这时候应该是app加载器),然后一路向上走到了bootstrap加载器,然后发现这个类是在核心jar包里的,如常见的 SPI 有 JDBC、JNDI等,这些 SPI 的接口属于 Java 核心库,一般存在rt.jar包中,由Bootstrap类加载器加载。但具体的方法在app加载器里。这时候系统会判断(比如遍历寻找),如果这个这个方法属于核心jar包,那么就调用这个线程,获取执行new Object的这个线程的加载器(比如app加载器),去加载具体的实现类。没设置的话默认是系统加载器,也就是app.
那么dubbo的SPI为什么需要用这种方式??
jar包是放在classpath底下的,也就是属于app加载器,那里面为什么要用线程类加载器来加载呢??看到一个回答但不知道对不对: