1. 什么是线程上下文类加载器?
线程上下文类加载器(Thread Context ClassLoader)简称 TCCL,是 Java 中一个重要的概念,它是 Java 中的一个类加载器,用于加载线程上下文中所需要的类。其本质上是一个普通的 Java 类,不同之处在于其拓展了 ClassLoader 基础类,提供了一些特殊的功能。线程上下文类加载器和普通类加载器不同的是,它不需要遵循双亲委派机制。
2. 为什么需要线程上下文类加载器?
在 Java 应用程序中,类的加载是由类加载器完成的,按照双亲委派机制的规则,子类加载器会向父级类加载器请求加载,如果父级类加载器不能完成加载操作,则会将其沿上继续传递给更高级别的父级类加载器,最终如果找不到对应类加载器,则系统将会抛出 ClassNotFoundException 异常。双亲委派机制一方面可以保证 Java 平台核心类的安全性,另一方面也可以保证 Java 核心类库的稳定性和正确性。
但是,有时候我们希望某些类的加载不受双亲委派机制的限制,比如一些框架或者插件就需要自己实现一套类加载机制。这时候就需要线程上下文类加载器来打破双亲委派机制,使得父类加载器可以访问到子类加载器加载的类。
3. 线程上下文类加载器的实现原理
线程上下文类加载器代码实现非常简单,它的主要思想是在需要访问的类的加载器不同的时候,通过线程上下文类加载器来进行操作。
public class Demo { public static void main(String[] args) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(new MyClassLoader()); // 执行操作 Thread.currentThread().setContextClassLoader(oldClassLoader); } }
在上面的代码中,我们首先获取了当前线程的上下文类加载器,然后将其重新设置成自定义的 MyClassLoader,执行一些需要访问不同类加载器的操作,最后需要将当前线程的上下文类加载器重新设置为原来的类加载器。
4. 线程上下文类加载器的使用示例
Java 中经典的线程上下文类加载器的案例就是 JDBC 规范。JDBC 规范中定义了一系列接口,这些接口通常由驱动程序提供实现。当 JDBC 的客户端在应用程序中加载驱动程序时,驱动程序的类加载器加载了具体的类,不同的数据库的驱动程序类在不同的类加载器中。
这样就可能出现了两种情况:
- 当前线程的类加载器为驱动程序类加载器,但是应用程序获取的数据库连接是由另一个类加载器加载的。
- 当前线程的类加载器为应用程序类加载器,但是驱动程序获取的数据库连接是由另一个类加载器加载的。
以上两种情况都会造成 ClassNotFoundException 异常。所以为了解决这种情况,JDBC 规范使用了线程上下文类加载器。
5. 线程上下文类加载器的优点
线程上下文类加载器解决了类加载机制中父子类加载器的限制,可以在不破坏双亲委派模型的基础上,允许线程在运行期间使用自己的加载器读取类库。
另外,线程上下文类加载器还可以增加程序的灵活性,在不同的容器中运行程序时,可以使用其特有的类加载器,加载不同环境需要的类,可以解决常见的 jar 冲突问题。
6. 线程上下文类加载器的缺点
线程上下文类加载器机制存在的问题主要是使用不当时降低性能的问题。如果不在必要的时候使用线程上下文类加载器,在获取类的时候会增大时间开销,因为是通过反射对类进行初始化的,这个操作需要一定的时间。
7. 线程上下文类加载器的使用注意事项
在线程中使用线程上下文类加载器时,需要注意以下几点:
- 在线程的运行期间,可以使用 setContextClassLoader()方法来设置线程上下文类加载器,线程结束后需要将其还原到原始类加载器,以免影响其他线程的正常运行。
- 如果在应用程序中使用自定义的类加载器,需要一些特殊的代码来隔离各个加载器。 在容器框架中,通常不需要直接操作线程上下文类加载器,而是使用如 Java 2 Platform Standard Edition(Edition)1.3.1 或更高版本中提供的 Thread.currentThread().getContextClassLoader()方法,将线程的上下文类加载器设置为当前线程的类加载器。
8. 总结
线程上下文类加载器是 Java 虚拟机提供的一个重要概念。其本质是一个 Java 类,用于加载线程上下文中所需要的类。它的作用是为了打破双亲委派机制,在某些情况下需要读取特定的类库或者线程需要使用特定的类库时,可以通过线程上下文类加载器实现。线程上下文类加载器有一些优点,比如可以对于同一份代码实现多次加载,以及解决不同类加载器的冲突等。但是,它也存在一些缺点,比如降低性能的问题。在使用线程上下文类加载器时,需要注意保存原始类加载器,并在合适的时候还原。同时,需要特别注意多个类加载器可能会导致冲突等问题,需要特别处理。
本文由 mdnice 多平台发布