java 上下文加载器_【深入理解Java虚拟机 】线程的上下文类加载器
線程上下文類加載器
線程上下文類加載器( Thread Context ClassLoader) 是從JDK1.2 引入的,類Thread 的getContextClassLoader() 與 setContextClassLoader(Classloader var1) 分別用來設(shè)置線程的上下文類加載器。如果沒有指定線程的上下文的加載器,那么線程將會繼承父線程的上下文類加載器。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 說起
在以前學(xué)習(xí)JDBC 的時候我們最深刻的印象應(yīng)該是 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導(dǎo)入的 jdbc 的相關(guān)類可以看到,他們均來自于 java.sql 包下,根據(jù)類的雙親委派機(jī)制可以非常清晰的知道,這些接口肯定是來自于 系統(tǒng)類加載器加載。那么具體的其實現(xiàn)類使用我們的應(yīng)用類加載器加載,所以問題出現(xiàn) 其中的JDBC 標(biāo)準(zhǔn)是由啟動器類加載器加載,而具體的實現(xiàn)的類是由系統(tǒng)類加載器加載,所以這就會導(dǎo)致啟動類加載器加載的JDBC標(biāo)準(zhǔn)類無法找到子加載器加載的JDBC實現(xiàn)
TCC 的作用: 改變雙親委托模型
上面的實現(xiàn)模式,我們稱之為SPI (服務(wù)提供接口) ,這種服務(wù)方式通過類的雙親委派機(jī)制就會出現(xiàn)問題。
這是加載器雙親委派模型的一個缺陷,但是JVM設(shè)計者做了一個不太優(yōu)雅的的方式解決,就是線程上下文類的加載器, ?父 ClassLoader 可以使用 Thread.currentThread().getContextClassloader() 所制定的Classloader 加載的類,這就改變了父ClassLoader不能使用子ClassLoader加載的類以及其他沒有父子關(guān)系的加載器加載類的訪問情況,即改變了雙親委托模型。
Java中所有涉及SPI的加載動作都采用這種方式, 實現(xiàn)方案包括: ?JDBC、JNDI、JCE以及JBI等
TCC 的使用模式
線程上下文的使用模式主要分為: 獲取-> 設(shè)置 -> 使用 -> 還原 , 偽代碼可以參考:
// 獲取原類加載器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 通過一些手段獲取的目標(biāo)類加載器
ClassLoader targetClassLoader = xxxx;
try {
// 將新的類加載器設(shè)置為當(dāng)前上下文類加載器
Thread.currentThread().setContextClassLoader(targetClassLoader);
// 使用加載器加載一些自己需要的類
loadClass();
} finally {
// 還原
Thread.currentThread().setContextClassLoader(classLoader);
}
總結(jié)
當(dāng)高層提供了統(tǒng)計的接口讓低層去實現(xiàn),同時又要在高層加載(或者實例化)這個類,那么就必須通過線程上下文類加載器幫助高層ClassLoader 加載這個類
父加載器不能訪問使用子加載器加載的類,子加載器可以訪問使用父加載器加載的類
就SPI服務(wù)而言,有些接口是啟動類加載器加載,但具體的實現(xiàn)各個廠商有自己不同的實現(xiàn)方式,這些實現(xiàn)是不會被啟動類加載器加載的,這樣傳統(tǒng)的雙親委托機(jī)制就無法滿足 SPI 的要求。而通過設(shè)置當(dāng)前線程的上下類加載器,就可以通過當(dāng)前線程的上下文類加載器加載這些類。
Java中所有涉及SPI的加載動作都采用這種方式, 實現(xiàn)方案包括: ?JDBC、JNDI、JCE以及JBI等
拓展學(xué)習(xí)點
Tomcat 中的類加載機(jī)制
SpringWeb 中類實現(xiàn)的機(jī)制
總結(jié)
以上是生活随笔為你收集整理的java 上下文加载器_【深入理解Java虚拟机 】线程的上下文类加载器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言实现离散余弦变换(DCT)并用MA
- 下一篇: git的clone命令出现fatal:u