最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中線程上下文類加載器超詳細(xì)講解使用

 更新時(shí)間:2022年12月22日 15:24:22   作者:Brycen Liu  
這篇文章主要介紹了Java中線程上下文類加載器,類加載器負(fù)責(zé)讀取Java字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例的代碼模塊。本文主要和大家聊聊JVM類加載器ClassLoader的使用,需要的可以了解一下

一、什么是線程上下文類加載器

線程上下文類加載器(Context Classloader)是從JDK1.2開始引入的,類Thread中的getContextClassLoader()和setContextClassLoader(ClassLoader cl)分別用來獲取和設(shè)置上線文類加載器。

如果沒有通過setContextClassLoader(ClassLoader cl)進(jìn)行設(shè)置的話,線程將繼承其父線程的上下文類加載器。

Java應(yīng)用運(yùn)行時(shí)的初始線程的上下文類加載器是系統(tǒng)類加載器。在線程中運(yùn)行的代碼可以通過該類加載器來加載類與資源。

1.1、重要性

它可以打破雙親委托機(jī)制,父ClassLoader可以使用當(dāng)前線程的Thread.currentThread().getContextClassLoader()所指定的classLoader來加載類,這就可以改變父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關(guān)系的ClassLoader加載的類的情況,即改變了雙親委托模型

1.2、使用場(chǎng)景

對(duì)于SPI來說,有些接口是Java核心庫(kù)所提供的,而Java核心庫(kù)是由啟動(dòng)類加載器加載的,而這些接口的實(shí)現(xiàn)卻是來自于不同jar包(廠商提供),Java的啟動(dòng)類加載是不會(huì)加載其他來源的jar包,這樣傳統(tǒng)的雙親委托模型就無法滿足SPI的要求。而通過給當(dāng)前線程設(shè)置上下文類加載器,就可以由設(shè)置的上線文類加載器來實(shí)現(xiàn)與借口哦實(shí)現(xiàn)類的加載。

二、ServiceLoader簡(jiǎn)單介紹

它是一個(gè)簡(jiǎn)單的加載服務(wù)提供者的機(jī)制。通常服務(wù)提供者會(huì)實(shí)現(xiàn)服務(wù)當(dāng)中所定義的接口。服務(wù)提供者可以以一種擴(kuò)展的jar包的形式安裝到j(luò)ava平臺(tái)上擴(kuò)展目錄中,也可以添加到應(yīng)用的classpath中。

  • 服務(wù)提供者需要提供一個(gè)無參數(shù)的構(gòu)造方法
  • 服務(wù)提供者是通過在META-INF/services目錄下相應(yīng)的提供者配置文件,該配置文件的文件名由服務(wù)接口的包名組成。
  • 提供者配置文件里面就是實(shí)現(xiàn)這個(gè)服務(wù)接口的類路徑,每個(gè)服務(wù)提供者占一行。
  • ServiceLoader是按需加載和實(shí)例化提供者的,就是懶加載,ServiceLoader其中還包含一個(gè)服務(wù)提供者緩存,里面存放著已經(jīng)加載的服務(wù)提供者。
  • ServiceLoader會(huì)返回一個(gè)iterator迭代器,會(huì)返回所有已經(jīng)加載了的服務(wù)提供者。
  • ServiceLoader是線程不安全的

問題分析:

服務(wù)的接口通常是由啟動(dòng)類加載器去加載的,那么它又是怎么去訪問到我們放在應(yīng)用classpath下的擴(kuò)展服務(wù)提供者的呢?

其內(nèi)部是通過掃描提供者配置文件,通過線程上下文類加載器來加載具體的實(shí)現(xiàn)類,線程上線文毋庸置疑默認(rèn)就是我們的系統(tǒng)類加載器,這樣就可以訪問到我們具體的服務(wù)提供者了。

三、案例

3.1、使用ServiceLoader加載mysql驅(qū)動(dòng)

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("當(dāng)前線程上線文類加載器:"+Thread.currentThread().getContextClassLoader());
        System.out.println("ServiceLoader類加載器:"+loader.getClass().getClassLoader());
    }
}

運(yùn)行結(jié)果:

Driver接口的兩個(gè)實(shí)現(xiàn)類是由系統(tǒng)類加載器加載的,而我們的ServiceLoader類加載又是啟動(dòng)類加載,此時(shí)正是因?yàn)槭褂镁€程類加載器中的系統(tǒng)類加載器。如果在加載之前,我們修改線程上線文類加載器為擴(kuò)展類加載器時(shí),那我們的兩個(gè)實(shí)現(xiàn)類就加載不了了。

class com.mysql.jdbc.Driver, 類加載器:sun.misc.Launcher$AppClassLoader@18b4aac2
class com.mysql.fabric.jdbc.FabricMySQLDriver, 類加載器:sun.misc.Launcher$AppClassLoader@18b4aac2
當(dāng)前線程上線文類加載器:sun.misc.Launcher$AppClassLoader@18b4aac2
ServiceLoader類加載器:null

3.2、Class.forName加載Mysql驅(qū)動(dòng)

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");
    }
}

3.2.1、com.mysql.jdbc.Driver

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
	//靜態(tài)代碼塊,初始化的時(shí)候會(huì)執(zhí)行
    static {
        try {
        	//主動(dòng)使用DriverManager,則該類也會(huì)初始化
        	//初始化完成后就調(diào)用DriverManager的registerDriver方法將自身添加到驅(qū)動(dòng)集合中。
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

3.2.2、java.sql.DriverManager初始化

由于上面主動(dòng)使用了DriverManager,那么該類也會(huì)初始化

public class DriverManager {
    // 注冊(cè)JDBC驅(qū)動(dòng)的集合
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    ...
	...
    static {
    	//當(dāng)初始化的時(shí)候會(huì)執(zhí)行該方法
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
    ...
    ...
    private static void loadInitialDrivers() {
        String drivers;
        //通過獲取系統(tǒng)參數(shù)來加載jdbc的驅(qū)動(dòng),如果沒有該參數(shù)則返回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來加載驅(qū)動(dòng),ServiceLoader已經(jīng)在上面講解過了
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try{
                	//這里會(huì)將加載到的驅(qū)動(dòng)保存到上面的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);
            }
        }
    }
    ...
    ...

3.2.3、調(diào)用DriverManager的registerDriver方法

當(dāng)我們的DriverManager初始化完成之后,com.mysql.jdbc.Driver中的靜態(tài)代碼塊就會(huì)執(zhí)行registerDriver方法,然后將自身注冊(cè)到registeredDrivers集合中去,這樣就完成了注冊(cè)驅(qū)動(dòng)了

注:顯而易見,從DriverManager中的loadInitialDrivers我們可以得知,我們及時(shí)不使用Class.forName(“com.mysql.jdbc.Driver”),mysql的驅(qū)動(dòng)也能被加載,這是因?yàn)楹笃趈dk使用了ServiceLoader

...
...
//這個(gè)方法在com.mysql.jdbc.Driver初始化的時(shí)候被調(diào)用
public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {
	//將驅(qū)動(dòng)注冊(cè)到registeredDrivers集合中去
    registerDriver(driver, null);
}
...
...

3.2.4、執(zhí)行DriverManager.getConnection方法

@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);
    }
	//調(diào)用getConnection,并把基本信息和調(diào)用者的class(這里就是我們的MyTest27.class)
	//Reflection.getCallerClass()是個(gè)本地方法,返回調(diào)用者的class
    return (getConnection(url, info, Reflection.getCallerClass()));
}
private static Connection getConnection(
    String url, java.util.Properties info, Class<?> caller) throws SQLException {
    //這里獲取調(diào)用者的類加載器,如果為null則獲取線程上下文類加載
    //從而實(shí)現(xiàn)能夠在DirverManager中訪問到放在我們classpath目錄下的驅(qū)動(dòng)
    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) {
        //判斷每一個(gè)驅(qū)動(dòng)是否有權(quán)限,這里的權(quán)限就是判斷該驅(qū)動(dòng)的類加載器
        //和上面獲取到的類加載器是否一致
        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");
}

到此這篇關(guān)于Java中線程上下文類加載器超詳細(xì)講解使用的文章就介紹到這了,更多相關(guān)Java線程上下文類加載器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Elasticsearch term 查詢之精確值搜索功能實(shí)現(xiàn)

    Elasticsearch term 查詢之精確值搜索功能實(shí)現(xiàn)

    term查詢是Elasticsearch中用于精確值搜索的一種基本方式,通過了解 term 查詢的工作原理和使用方法,你可以更好地利用 Elasticsearch 進(jìn)行結(jié)構(gòu)化數(shù)據(jù)的搜索和分析,本文將詳細(xì)介紹 term 查詢的工作原理、使用場(chǎng)景以及如何在 Elasticsearch 中應(yīng)用它,感興趣的朋友一起看看吧
    2024-06-06
  • 使用IDEA創(chuàng)建Servlet程序的詳細(xì)步驟

    使用IDEA創(chuàng)建Servlet程序的詳細(xì)步驟

    在學(xué)習(xí)servlet過程中,參考的教程是用eclipse完成的,而我在練習(xí)的過程中是使用IDEA的,在創(chuàng)建servlet程序時(shí)遇到了挺多困難,在此記錄一下如何用IDEA完整創(chuàng)建一個(gè)servlet程序,感興趣的朋友一起看看吧
    2024-08-08
  • MyBatis-Plus常見面試題和答案大全

    MyBatis-Plus常見面試題和答案大全

    Mybatis-Plus是一個(gè)基于Mybatis的增強(qiáng)工具,它簡(jiǎn)化了Mybatis的開發(fā)流程,提供了許多實(shí)用的功能,如自動(dòng)生成代碼、分頁(yè)查詢、條件構(gòu)造器、性能分析等,這篇文章主要給大家介紹了關(guān)于MyBatis-Plus常見面試題和答案的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Spring?Boot如何監(jiān)控SQL運(yùn)行情況?

    Spring?Boot如何監(jiān)控SQL運(yùn)行情況?

    Druid是Java語言中最好的數(shù)據(jù)庫(kù)連接池,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot如何監(jiān)控SQL運(yùn)行情況的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Java事件處理步驟講解

    Java事件處理步驟講解

    今天小編就為大家分享一篇關(guān)于Java事件處理步驟講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • springboot jasypt2.x與jasypt3.x的使用方式

    springboot jasypt2.x與jasypt3.x的使用方式

    在軟件開發(fā)中,將配置文件中的敏感信息(如數(shù)據(jù)庫(kù)密碼)進(jìn)行加密是保障安全的有效手段,jasypt框架提供了這一功能,支持通過加密工具類或命令行工具生成密文,并通過修改配置文件和啟動(dòng)參數(shù)的方式使用密文和密鑰,這樣即便配置文件被泄露
    2024-09-09
  • springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    這篇文章主要介紹了springboot 實(shí)現(xiàn)mqtt物聯(lián)網(wǎng),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • java如何讀取resources目錄和同級(jí)目錄文件

    java如何讀取resources目錄和同級(jí)目錄文件

    介紹了Java中讀取resources目錄和同級(jí)目錄文件的方法,并討論了在IDE和發(fā)布環(huán)境中可能遇到的問題,通過測(cè)試發(fā)現(xiàn),執(zhí)行目錄可能會(huì)影響文件讀取,建議在使用`user.dir`時(shí)注意jar包的運(yùn)行目錄問題
    2024-12-12
  • Java如何使用Query動(dòng)態(tài)拼接SQL詳解

    Java如何使用Query動(dòng)態(tài)拼接SQL詳解

    這篇文章主要給大家介紹了關(guān)于Java如何使用Query動(dòng)態(tài)拼接SQL的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • java中文亂碼之解決URL中文亂碼問題的方法

    java中文亂碼之解決URL中文亂碼問題的方法

    這篇文章主要為大家分享了介紹了java中文亂碼之解決URL中文亂碼問題的方法,感興趣的小伙伴們可以參考一下
    2016-06-06

最新評(píng)論

来凤县| 海城市| 日土县| 应城市| 勐海县| 宽城| 依兰县| 万宁市| 湖南省| 道孚县| 新宁县| 罗田县| 安庆市| 石景山区| 临城县| 延长县| 巴南区| 象州县| 台州市| 维西| 江北区| 慈利县| 四子王旗| 南郑县| 临颍县| 满洲里市| 屯昌县| 新巴尔虎左旗| 吴忠市| 石门县| 灵寿县| 夏邑县| 离岛区| 嵊泗县| 武穴市| 班玛县| 珲春市| 延长县| 阿城市| 武平县| 合山市|