在复习前面的类加载机制时发现对线程上下文类加载器的概念很模糊,所以今天再来总结一下
线程上下文类加载器
我们知道JVM虚拟机采用双亲委派模式来加载类,而且在类加载的整个过程中只有在加载阶段可以别程序员操作,加载器通过类的全限定名在class文件的二进制流中加载类,并创建类的唯一一个class对象,作为类的全局访问点。我们知道为了实现程序的动态性,我们可以自定义类加载器,通过重写findClas()方法来实现自定义类加载器,再通过重写loadClass()方法打破双亲委派机制,这是一次打破双亲委派,那么还有一种就是使用线程上下文类加载器来打破双亲委派机制。
典型应用就是我们知道java提供一些第三方服务接口(SPI
),用的最多的就是jdbc接口,提供数据库服务,允许不同的第三方来实现,但是我们需要来java代码中加载第三方驱动,获得具体的实现代码
使用jdbcDriver连接数据库
(1)首先要导入数据库jar包
(2)加载驱动,创建连接
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/tsetjdbc", "root",
"123456");
其实我们发现没有第一句也能正常执行,原因就是线程上下文类加载器。
那么就是第二句,我们知道当调用DriverManager的静态方法getConnection()方法,会触动类的初始化,并且执行静态代码块,在DriverManager类中有如下静态代码块,里面调用了loadInitialDrivers()方法,
loadInitialDrivers()方法如下:我们看到使用ServiceLoader类来加载,那么我们就来介绍一下这个类
java.util.ServiceLoader(SPI加载的工具类)
SPI规定使用SPI实现类比价导入驱动jar包,就是我们使用数据库第一步要做的事情,然后会生成META-INF文件夹,下面生成services包里面生成对应的文件(service文件),然后通过ServiceLoader的load()方法来加载。
ServiceLoader的load()方法源码分析
可以看到这个类专门用来加载上述所说文件夹下面的类
然后通过前缀加service名字来加载类
load()方法。可以看到使用线程上下文来加载每一个service名字对应的类,我们知道ServiceLoader类是
又加载在BootrapLoader中,但是这写service来的jar路径又是在用户目录下,应该用ApplicationClassLoader来加载,但是根据双亲委派BootrapLoader类加载不到,
这时候就会使用线程上下文类加载器,在JVM中会把当前线程的类加载器加载不到的类交给线程上下文类加载器来加载,直接使用Thread.currentThread().getContextClassLoader()来获得,默认返回的就是应用程序类加载器,也可以通过java.lang.Thread类的setContextClassLoader()方法进行设置
总结:
线程上下文类加载器,可以打破双亲委派机制,实现逆向调用类加载器来加载当前线程中类加载器加载不到的类
热门工具 换一换