SPI与contextclassloader
文章目录
目标
通过SPI学习setContextClassLoader,getContextClassLoader的用法
SPI概念
SPI(Service Provider Interface)是JDK内置的一种提供服务发现的机制。 也可以看成是一种服务注册或者ioc
自定义SPI方式
- 定义一个接口
fluffy.mo.CarPlugin
- 定义两个实现类
fluffy.mo.RedCar
、fluffy.mo.BlueCar
- 配置文件 【META-INF/services/文件名=接口全名】
META-INF/services/fluffy.mo.CarPlugin
1 2
fluffy.mo.RedCar fluffy.mo.BlueCar
- 代码加载
ServiceLoader.load(fluffy.mo.CarPlugin.class);
相关案例: 各类jdbc驱动,比如mysql的驱动jar。 lombok也是通过注册插件到javac中,实现编译时修改字节码。
具体案例代码
现有一段测试代码, classpath中含有h2的驱动jar 也可以是mysql的驱动jar
|
|
运行结果为
|
|
案例代码分析
DriverManager 位于rt.jar中,类加载器为null , 其类加载器为BootstrapClassloader。
方法-DriverManager.loadInitialDrivers
|
|
代码ServiceLoader.load(Driver.class)
这里去加载了h2的驱动
如无意外 org.h2.Driver
的类加载器应该是BootstrapClassloader
但是在双亲委派的机制下, BootstrapClassloader
作为顶级类加载器只能自己去加载,不会去让子孙AppClassLoader
去加载。
此时BootstrapClassloader
应当加载失败
那为啥没挂呢? 代码再深入一下ServiceLoader.load
方法
补充:
代理链: AppClassloader -> ExtClassloader -> BootstrapClassloader
AppClassLoader
加载类时,会先找父类加载器,父类加载器加载成功则结束
不成功则AppClassLoader
自己去加载通常我们写的代码以及相关框架jar包都是在classpath目录下,由
AppClassLoader
去加载。
而BootstrapClassloader
只会去加载java的核心jar,不会去看classpath目录。
方法-ServiceLoader.load
|
|
事实上呢,ServiceLoader
在加载驱动类时,指定了当前线程的类加载器。
在没有setContextClassLoader的情况下默认为SystemClassLoader
也就是AppClassLoader
小结
java.lang.Thread
有两个方法.getContextClassLoader(), setContextClassLoader()
通过这两个方法,使用当前线程中转了类加载器,解决了父classloader无法将加载class的任务交给子classloader的问题。
(ps: 通过编码的方式使用类加载器去加载类,自然也能绕开BootstrapClassloader
类加载器。)
文章作者 duansheli
上次更新 2019-12-25 (325c7b3)