java 上下文加载器_【深入理解Java虚拟机 】线程的上下文类加载器
線程上下文類加載器
線程上下文類加載器( Thread Context ClassLoader) 是從JDK1.2 引入的,類Thread 的getContextClassLoader() 與 setContextClassLoader(Classloader var1) 分別用來設置線程的上下文類加載器。如果沒有指定線程的上下文的加載器,那么線程將會繼承父線程的上下文類加載器。Java 的初始化線程的上下文加載器,可以通過上下文類加載器加載類與資源。
基本的獲取和使用方法:
public class ContextClassLoader {
/**
* 每個類都會使用自己的類加載器嘗試去加載所依賴的類
*
*
如果ClassX 依賴了 ClassY ,那么在ClassX的加載器將會在主動引用ClassY 并且ClassY尚未被加載的時候加載ClassY 這個類
*/
public static void main(String[] args) {
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(Thread.class.getClassLoader());
}
}
從 JDBC 說起
在以前學習JDBC 的時候我們最深刻的印象應該是 Class.forName("xxxxxx"); 簡單的偽代碼如下圖:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class DbUtil {
public static final String URL = "jdbc:mysql://localhost:3306/db";
public static final String USER = "root";
public static final String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM student");
}
}
}
從上面的代碼中,import導入的 jdbc 的相關類可以看到,他們均來自于 java.sql 包下,根據類的雙親委派機制可以非常清晰的知道,這些接口肯定是來自于 系統類加載器加載。那么具體的其實現類使用我們的應用類加載器加載,所以問題出現 其中的JDBC 標準是由啟動器類加載器加載,而具體的實現的類是由系統類加載器加載,所以這就會導致啟動類加載器加載的JDBC標準類無法找到子加載器加載的JDBC實現
TCC 的作用: 改變雙親委托模型
上面的實現模式,我們稱之為SPI (服務提供接口) ,這種服務方式通過類的雙親委派機制就會出現問題。
這是加載器雙親委派模型的一個缺陷,但是JVM設計者做了一個不太優雅的的方式解決,就是線程上下文類的加載器, ?父 ClassLoader 可以使用 Thread.currentThread().getContextClassloader() 所制定的Classloader 加載的類,這就改變了父ClassLoader不能使用子ClassLoader加載的類以及其他沒有父子關系的加載器加載類的訪問情況,即改變了雙親委托模型。
Java中所有涉及SPI的加載動作都采用這種方式, 實現方案包括: ?JDBC、JNDI、JCE以及JBI等
TCC 的使用模式
線程上下文的使用模式主要分為: 獲取-> 設置 -> 使用 -> 還原 , 偽代碼可以參考:
// 獲取原類加載器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 通過一些手段獲取的目標類加載器
ClassLoader targetClassLoader = xxxx;
try {
// 將新的類加載器設置為當前上下文類加載器
Thread.currentThread().setContextClassLoader(targetClassLoader);
// 使用加載器加載一些自己需要的類
loadClass();
} finally {
// 還原
Thread.currentThread().setContextClassLoader(classLoader);
}
總結
當高層提供了統計的接口讓低層去實現,同時又要在高層加載(或者實例化)這個類,那么就必須通過線程上下文類加載器幫助高層ClassLoader 加載這個類
父加載器不能訪問使用子加載器加載的類,子加載器可以訪問使用父加載器加載的類
就SPI服務而言,有些接口是啟動類加載器加載,但具體的實現各個廠商有自己不同的實現方式,這些實現是不會被啟動類加載器加載的,這樣傳統的雙親委托機制就無法滿足 SPI 的要求。而通過設置當前線程的上下類加載器,就可以通過當前線程的上下文類加載器加載這些類。
Java中所有涉及SPI的加載動作都采用這種方式, 實現方案包括: ?JDBC、JNDI、JCE以及JBI等
拓展學習點
Tomcat 中的類加載機制
SpringWeb 中類實現的機制
總結
以上是生活随笔為你收集整理的java 上下文加载器_【深入理解Java虚拟机 】线程的上下文类加载器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言实现离散余弦变换(DCT)并用MA
- 下一篇: git的clone命令出现fatal:u