线程上下文类加载器(context class loader)是从JDK 1.2 开始引入的。类java.lang.Thread中的方法getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。
前面提到的类加载器的代理模式并不能解决Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的SPI 有JDBC、JCE、JNDI、JAXP 和JBI 等。这些SPI 的接口由Java 核心库来提供,如JAXP 的SPI 接口定义包含在javax.xml.parsers包中。这些SPI 的实现代码很可能是作为Java 应用所依赖的jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了JAXP SPI 的
Apache Xerces所包含的jar 包。SPI 接口中的代码经常需要加载具体的实现类。如JAXP 中的javax.xml.parsers.DocumentBuilderFactory类中的newInstance()方法用来生成一个新的DocumentBuilderFactory的实例。这里的实例的真正的类是继承自javax.xml.parsers.DocumentBuilderFactory,由SPI 的实现所提供的。如在Apache Xerces 中,实现的类是org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而问题在于,SPI 的接口是Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到SPI 的实现类的,因为它只加载Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。
线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到SPI 实现的类。线程上下文类加载器在很多SPI 的实现中都会用到。
作者 小诺N