線程上下文類加載器
Java 提供了很多服務提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實現。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。
這些 SPI 的接口由 Java 核心庫來提供,而這些 SPI 的實現代碼則是作為 Java 應用所依賴的 jar 包被包含進類路徑(CLASSPATH)裏。SPI接口中的代碼經常需要加載具體的實現類。那麽問題來了,SPI的接口是Java核心庫的一部分,是由啟動類加載器來加載的;SPI的實現類是由系統類加載器來加載的。啟動類加載器是無法找到 SPI 的實現類的,因為它只加載 Java 的核心庫。它也不能委派給系統類加載器,因為它是系統類加載器的祖先類加載器。
線程上下文類加載器正好解決了這個問題。如果不做任何的設置,Java 應用的線程的上下文類加載器默認就是系統上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實現的類。線程上下文類加載器在很多 SPI 的實現中都會用到。
而線程上下文類加載器破壞了“雙親委派模型”,可以在執行線程中拋棄雙親委派加載鏈模式,使程序可以逆向使用類加載器。
線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的。類 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設置線程的上下文類加載器。如果沒有通過 setContextClassLoader(ClassLoader cl)方法進行設置的話,線程將繼承其父線程的上下文類加載器。Java 應用運行的初始線程的上下文類加載器是系統類加載器。在線程中運行的代碼可以通過此類加載器來加載類和資源。
拿JNDI為例,它的核心是由JRE核心類(rt.jar)實現的,要由啟動類加載器去加載。但這些核心JNDI類必須能加載由第三方廠商提供的JNDI實現,但是這個不能用啟動類加載器去加載。解決辦法就是讓核心JNDI類使用線程上下文類加載器,從而有效的打通類加載器層次結構,逆著代理機制的方向使用類加載器。
線程上下文類加載器