破坏双亲委派机制及SPI源码解析

双亲委派机制的目的是防止类的重复加载,使类加载具有优先级的层次关系,但是这种机制有时候会有弊端:
因为类加载有一条机制:当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类, 设想在这种情况下java.sql.DriverManager是由BootStrapClassLoader加载的,那么也会用BootStraoClassLoader去加载com.mysql.cj.jdbc.Driver(假如当前驱动是Mysql),而第三方依赖原则上来说是应该由AppClassLoader来加载的,用BootStrapClassLoader就会加载不到,解决办法是引入了SPI(ServiceLoader)的机制,下面附上源码

java.sql.DriverManager#loadInitialDrivers
在这里插入图片描述
破坏双亲委派机制及SPI源码解析_第1张图片
这里先调用load返回一个ServiceLoader,然后获取到iterator调用next,其实这里的iterator就是ServiceLoader.LazyIterator

java.util.ServiceLoader.LazyIterator#next
破坏双亲委派机制及SPI源码解析_第2张图片
这里调用了nextService()方法:可以看到加载了nextName,而这个nextName是什么呢? 其实就是mysql-connector-java/META-INF/services/java.sql.Driver中的配置的内容:com.mysql.cj.jdbc.Driver,使用迭代器是因为看这个文件中可以配置多个Driver的实现类破坏双亲委派机制及SPI源码解析_第3张图片
破坏双亲委派机制及SPI源码解析_第4张图片
hasNextService():fullName就是在META-INF/services/目录下的文件名
破坏双亲委派机制及SPI源码解析_第5张图片
而类加载器是怎样加载到我们项目目录下的这个实现类呢? 再回到最开始的loadInitialDrivers方法,里面调用的erviceLoader.load(Driver.class)方法:
在这里插入图片描述
ClassLoader cl = Thread.currentThread().getContextClassLoader(); 这句代码就是获取到了线程上下文类加载器,也就是用这个加载器来加载java.sql.Driver和各数据库厂商的实现类, 而这个线程上下文类加载器默认情况下就是AppClassLoader

总结
BootStrapClassLoader加载DriverManager,却通过线程上下文类加载器来加载com.mysql.cj.jdbc.Driver,这是一种父类加载器去请求子类加载器完成类加载的行为,实际上是打破了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的一般性原则, 但也是无可奈何的事情

你可能感兴趣的:(破坏双亲委派机制及SPI源码解析)