<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
執行緒上下文類載入器(Context Classloader)是從JDK1.2開始引入的,類Thread中的getContextClassLoader()和setContextClassLoader(ClassLoader cl)分別用來獲取和設定上線文類載入器。
如果沒有通過setContextClassLoader(ClassLoader cl)進行設定的話,執行緒將繼承其父執行緒的上下文類載入器。
Java應用執行時的初始執行緒的上下文類載入器是系統類載入器。線上程中執行的程式碼可以通過該類載入器來載入類與資源。
它可以打破雙親委託機制,父ClassLoader可以使用當前執行緒的Thread.currentThread().getContextClassLoader()所指定的classLoader來載入類,這就可以改變父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關係的ClassLoader載入的類的情況,即改變了雙親委託模型
對於SPI來說,有些介面是Java核心庫所提供的,而Java核心庫是由啟動類載入器載入的,而這些介面的實現卻是來自於不同jar包(廠商提供),Java的啟動類載入是不會載入其他來源的jar包,這樣傳統的雙親委託模型就無法滿足SPI的要求。而通過給當前執行緒設定上下文類載入器,就可以由設定的上線文類載入器來實現與藉口哦實現類的載入。
它是一個簡單的載入服務提供者的機制。通常服務提供者會實現服務當中所定義的介面。服務提供者可以以一種擴充套件的jar包的形式安裝到java平臺上擴充套件目錄中,也可以新增到應用的classpath中。
問題分析:
服務的介面通常是由啟動類載入器去載入的,那麼它又是怎麼去存取到我們放在應用classpath下的擴充套件服務提供者的呢?
其內部是通過掃描提供者組態檔,通過執行緒上下文類載入器來載入具體的實現類,執行緒上線文毋庸置疑預設就是我們的系統類載入器,這樣就可以存取到我們具體的服務提供者了。
package com.brycen.classloader; import java.sql.Driver; import java.util.Iterator; import java.util.ServiceLoader; public class MyTest26 { public static void main(String[] args) { ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class); Iterator<Driver> iterator = loader.iterator(); while (iterator.hasNext()){ Driver dirver = iterator.next(); System.out.println(dirver.getClass()+", 類載入器:"+dirver.getClass().getClassLoader()); } System.out.println("當前執行緒上線文類載入器:"+Thread.currentThread().getContextClassLoader()); System.out.println("ServiceLoader類載入器:"+loader.getClass().getClassLoader()); } }
執行結果:
Driver介面的兩個實現類是由系統類載入器載入的,而我們的ServiceLoader類載入又是啟動類載入,此時正是因為使用執行緒類載入器中的系統類載入器。如果在載入之前,我們修改執行緒上線文類載入器為擴充套件類載入器時,那我們的兩個實現類就載入不了了。
class com.mysql.jdbc.Driver, 類載入器:sun.misc.Launcher$AppClassLoader@18b4aac2
class com.mysql.fabric.jdbc.FabricMySQLDriver, 類載入器:sun.misc.Launcher$AppClassLoader@18b4aac2
當前執行緒上線文類載入器:sun.misc.Launcher$AppClassLoader@18b4aac2
ServiceLoader類載入器:null
public class MyTest27 { public static void main(String[] args) throws ClassNotFoundException, SQLException { //載入並初始化com.mysql.jdbc.Driver Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password"); } }
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } //靜態程式碼塊,初始化的時候會執行 static { try { //主動使用DriverManager,則該類也會初始化 //初始化完成後就呼叫DriverManager的registerDriver方法將自身新增到驅動集合中。 DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
由於上面主動使用了DriverManager,那麼該類也會初始化
public class DriverManager { // 註冊JDBC驅動的集合 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); ... ... static { //當初始化的時候會執行該方法 loadInitialDrivers(); println("JDBC DriverManager initialized"); } ... ... private static void loadInitialDrivers() { String drivers; //通過獲取系統引數來載入jdbc的驅動,如果沒有該引數則返回null try { drivers = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //通過ServiceLoader來載入驅動,ServiceLoader已經在上面講解過了 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ //這裡會將載入到的驅動儲存到上面的registeredDrivers集合中去 while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing } return null; } }); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } String[] driversList = drivers.split(":"); println("number of Drivers:" + driversList.length); for (String aDriver : driversList) { try { println("DriverManager.Initialize: loading " + aDriver); Class.forName(aDriver, true, ClassLoader.getSystemClassLoader()); } catch (Exception ex) { println("DriverManager.Initialize: load failed: " + ex); } } } ... ...
當我們的DriverManager初始化完成之後,com.mysql.jdbc.Driver中的靜態程式碼塊就會執行registerDriver方法,然後將自身註冊到registeredDrivers集合中去,這樣就完成了註冊驅動了
注:顯而易見,從DriverManager中的loadInitialDrivers我們可以得知,我們及時不使用Class.forName(“com.mysql.jdbc.Driver”),mysql的驅動也能被載入,這是因為後期jdk使用了ServiceLoader
... ... //這個方法在com.mysql.jdbc.Driver初始化的時候被呼叫 public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { //將驅動註冊到registeredDrivers集合中去 registerDriver(driver, null); } ... ...
@CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); //封裝使用者名稱和密碼 if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } //呼叫getConnection,並把基本資訊和呼叫者的class(這裡就是我們的MyTest27.class) //Reflection.getCallerClass()是個本地方法,返回撥用者的class return (getConnection(url, info, Reflection.getCallerClass())); } private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { //這裡獲取呼叫者的類載入器,如果為null則獲取執行緒上下文類載入 //從而實現能夠在DirverManager中存取到放在我們classpath目錄下的驅動 ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection("" + url + "")"); SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) { //判斷每一個驅動是否有許可權,這裡的許可權就是判斷該驅動的類載入器 //和上面獲取到的類載入器是否一致 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } // if we got here nobody could connect. if (reason != null) { println("getConnection failed: " + reason); throw reason; } println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); }
到此這篇關於Java中執行緒上下文類載入器超詳細講解使用的文章就介紹到這了,更多相關Java執行緒上下文類載入器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45