当前类加载器(Current Classloader)
每一个类都会使用自己的类加载器(即加载自身的类加载器)来去加载其他类(指的是所依赖的类)如果ClassX引用了ClassY,那么ClassX的类加载器就会去加载ClassY(前提是ClassY尚未被加载)
线程上下文类加载器(Context Classloader)
线程上下文类加载器是由jdk1.2引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别用来获取喝设置上下文类加载器。如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文类加载器。
Java应用运行时的初始线程的上下文类加载器时系统类加载器。在线程中运行的代码可以通过该类加载器来加载类与资源。
线程上下文类加载器的重要性:
SPI ( Service Provider Interface )
父ClassLoader可以使用当前线程Thread.currentThread().getContextLoader()所指定的classLoader加载的类。这就改变了父ClassLoader不能使用子ClassLoader或者其他没有直接父子关系的ClassLoader加载的类的情况,即改变了双亲委托模型。
线程上下文类加载器就是当前线程的Current ClassLoader
在双亲委托模式下,类加载时由下向上的,即下层的类加载器会委托上层的类加载器进行加载。但对于SPI来说,有些接口是Java核心库提供的,而java核心库是由启动类加载器进行加载,而这些接口的实现却是来自于不同的厂商提供的jar包,java的启动类加载器是不会加载这些jar包,这样传统的双亲委托模式就无法满足SPI的要求,而通过当前线程设置上下文类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载
例如:jdbc的连接就是在jdk中声明了接口,但是实现却是由不通厂商进行编写的
线程上下文加载器的一般使用模式(获取 - 使用 - 还原)
ClassLoader calssLoader = Thread.currentThread().getContextClassLoader();
try {
//设置线程上下文类加载器为自定义的加载器
Thread.currentThread.setContextClassLoader(targetTccl);
myMethod(); //执行自定义的方法
} finally {
//还原线程上下文类加载器
Thread.currentThread().setContextClassLoader(classLoader);
}
myMethod里面调用了Thread.currentThread().getContextClassLoader(),获取当前线程上下文类加载器做某些事情。如果一个类由类加载器A加载,那么这个依赖类也是由相同的类加载的(如果该依赖类之前没有被加载过的话)
ContextClassLoader的作用就是为了破坏加载委托机制。
当高层提供了统一的接口让低层去实现,同时又要在高层加载(实例化)底层的类时,就必须要通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。