欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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開(kāi)始引入的,類Thread中的getContextClassLoader()和setContextClassLoader(ClassLoader cl)分別用來(lái)獲取和設(shè)置上線文類加載器。

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

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

1.1、重要性

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

1.2、使用場(chǎng)景

對(duì)于SPI來(lái)說(shuō),有些接口是Java核心庫(kù)所提供的,而Java核心庫(kù)是由啟動(dòng)類加載器加載的,而這些接口的實(shí)現(xiàn)卻是來(lái)自于不同jar包(廠商提供),Java的啟動(dòng)類加載是不會(huì)加載其他來(lái)源的jar包,這樣傳統(tǒng)的雙親委托模型就無(wú)法滿足SPI的要求。而通過(guò)給當(dāng)前線程設(shè)置上下文類加載器,就可以由設(shè)置的上線文類加載器來(lái)實(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è)無(wú)參數(shù)的構(gòu)造方法
  • 服務(wù)提供者是通過(guò)在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èn)題分析:

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

其內(nèi)部是通過(guò)掃描提供者配置文件,通過(guò)線程上下文類加載器來(lái)加載具體的實(shí)現(xiàn)類,線程上線文毋庸置疑默認(rèn)就是我們的系統(tǒng)類加載器,這樣就可以訪問(wèn)到我們具體的服務(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;
        //通過(guò)獲取系統(tǒng)參數(shù)來(lái)加載jdbc的驅(qū)動(dòng),如果沒(méi)有該參數(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() {
				//通過(guò)ServiceLoader來(lái)加載驅(qū)動(dòng),ServiceLoader已經(jīng)在上面講解過(guò)了
                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)了

注:顯而易見(jiàn),從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中訪問(wèn)到放在我們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)文章

  • idea在用Mybatis時(shí)xml文件sql不提示解決辦法(提示后背景顏色去除)

    idea在用Mybatis時(shí)xml文件sql不提示解決辦法(提示后背景顏色去除)

    mybatis的xml文件配置的時(shí)候,有時(shí)候會(huì)沒(méi)有提示,這讓我們很頭疼,下面這篇文章主要給大家介紹了關(guān)于idea在用Mybatis時(shí)xml文件sql不提示的解決辦法,提示后背景顏色去除的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Java JUC中操作List安全類的集合案例

    Java JUC中操作List安全類的集合案例

    這篇文章主要介紹了JUC中操作List安全類的集合案例,本文羅列了不安全的集合和安全的集合進(jìn)行對(duì)比,以及Java中提供的安全措施,需要的朋友可以參考下
    2021-07-07
  • spring boot 集成shiro的配置方法

    spring boot 集成shiro的配置方法

    要在spring boot上集成其他框架,首先要會(huì)spring javaconfig方法,利用此方法同樣可以配置其他模塊。這篇文章主要介紹了spring boot 集成shiro的配置方法,需要的朋友可以參考下
    2018-01-01
  • java利用遞歸算法實(shí)現(xiàn)對(duì)文件夾的刪除功能

    java利用遞歸算法實(shí)現(xiàn)對(duì)文件夾的刪除功能

    這篇文章主要介紹了java利用遞歸算法實(shí)現(xiàn)對(duì)文件夾的刪除功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 基于ArrayList常用方法的源碼全面解析

    基于ArrayList常用方法的源碼全面解析

    下面小編就為大家?guī)?lái)一篇基于ArrayList常用方法的源碼全面解析。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Java重寫與重載之間的區(qū)別

    Java重寫與重載之間的區(qū)別

    本文主要介紹了Java重寫與重載之間的區(qū)別。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • java 頁(yè)面url傳值中文亂碼的解決方法

    java 頁(yè)面url傳值中文亂碼的解決方法

    本節(jié)講的是ajax 的URL參數(shù)中有中文值,傳到服務(wù)端,在用request.getParameter()方法,得到的常常會(huì)是亂碼。
    2013-03-03
  • 利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐

    利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐

    這篇文章主要給大家介紹了關(guān)于利用MyBatis-Plus靈活處理JSON字段的技巧與最佳實(shí)踐,Mybatis-Plus可以很方便地處理JSON字段,在實(shí)體類中可以使用@JSONField注解來(lái)標(biāo)記JSON字段,需要的朋友可以參考下
    2024-07-07
  • java實(shí)現(xiàn)將字符串中首字母轉(zhuǎn)換成大寫,其它全部轉(zhuǎn)換成小寫的方法示例

    java實(shí)現(xiàn)將字符串中首字母轉(zhuǎn)換成大寫,其它全部轉(zhuǎn)換成小寫的方法示例

    這篇文章主要介紹了java實(shí)現(xiàn)將字符串中首字母轉(zhuǎn)換成大寫,其它全部轉(zhuǎn)換成小寫的方法,涉及java字符串遍歷、轉(zhuǎn)換、拼接等相關(guān)操作技巧,需要的朋友可以參考下
    2019-06-06
  • jar包沖突常用的解決方案

    jar包沖突常用的解決方案

    引言在使用java語(yǔ)言開(kāi)發(fā),maven做項(xiàng)目管理時(shí),我們經(jīng)常遇到一個(gè)頭疼的問(wèn)題就是jar包沖突,這篇文章主要給大家介紹了關(guān)于jar包沖突常用的解決方案,需要的朋友可以參考下
    2023-12-12

最新評(píng)論