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

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

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

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

总结
通过获取线程上下文类加载器,也即AppClassLoader来加载java.sql.Driver类和com.mysql.cj.jdbc.Driver等,
最终打破了双亲委派模型