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

Java類加載器之ContextClassLoader詳解

 更新時(shí)間:2023年10月07日 10:22:17   作者:先說好不能罵我  
這篇文章主要介紹了Java類加載器之ContextClassLoader詳解,ContextClassLoader是一種與線程相關(guān)的類加載器,類似ThreadLocal,每個(gè)線程對應(yīng)一個(gè)上下文類加載器,需要的朋友可以參考下

ContextClassLoader

ContextClassLoader是一種與線程相關(guān)的類加載器,類似ThreadLocal,每個(gè)線程對應(yīng)一個(gè)上下文類加載器.在實(shí)際使用時(shí)一般都用下面的經(jīng)典結(jié)構(gòu):

ClassLoader targetClassLoader = null;// 外部參數(shù)
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(targetClassLoader);
    // TODO
} catch (Exception e) {
    e.printStackTrace();
} finally {
    Thread.currentThread().setContextClassLoader(contextClassLoader);
}
  • 首先獲取當(dāng)前線程的線程上下文類加載器并保存到方法棧,然后將外部傳遞的類加載器設(shè)置為當(dāng)前線程上下文類加載器
  • doSomething則可以利用新設(shè)置的類加載器做一些事情
  • 最后在設(shè)置當(dāng)前線程上下文類加載器為老的類加載器

上面的使用場景是什么?Java默認(rèn)的類加載機(jī)制是委托機(jī)制,但是有些時(shí)候需要破壞這種固定的機(jī)制

具體來說,比如Java中的SPI(Service Provider Interface)是面向接口編程的,服務(wù)規(guī)則提供者會在JRE的核心API里面提供服務(wù)訪問接口,而具體的實(shí)現(xiàn)則由其他開發(fā)商提供.我們知道Java核心API,比如rt.jar包,是使用Bootstrap ClassLoader加載的,而用戶提供的jar包再有AppClassLoader加載.并且我們知道一個(gè)類由類加載器A加載,那么這個(gè)類依賴類也應(yīng)該由相同的類加載器加載.那么Bootstrap ClassLoader加載了服務(wù)提供者在rt.jar里面提供的搜索開發(fā)上提供的實(shí)現(xiàn)類的API類(ServiceLoader),那么這些API類里面依賴的類應(yīng)該也是有Bootstrap ClassLoader來加載.而上面說了用戶提供的Jar包有AppClassLoader加載,所以需要一種違反雙親委派模型的方法,線程上下文類加載器ContextClassLoader就是為了解決這個(gè)問題.

下面使用JDBC來具體說明,JDBC是基于SPI機(jī)制來發(fā)現(xiàn)驅(qū)動提供商提供的實(shí)現(xiàn)類,提供者只需在JDBC實(shí)現(xiàn)的jar的META-INF/services/java.sql.Driver文件里指定實(shí)現(xiàn)類的方式暴露驅(qū)動提供者.例如:MYSQL實(shí)現(xiàn)的jar如下:

這里寫圖片描述

其中MYSQL的驅(qū)動如下實(shí)現(xiàn)了java.sql.Driver

public class Driver extends NonRegisteringDriver implements java.sql.Driver

引入MySQL驅(qū)動的jar包,測試類如下:

import java.sql.Driver;
import java.util.Iterator;
import java.util.ServiceLoader;
public class MySQLClassLoader {
    public static void main(String[] args) {
        ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
        Iterator<Driver> iterator = loader.iterator();
        while (iterator.hasNext()) {
            Driver driver = (Driver) iterator.next();
            System.out.println("driver:" + driver.getClass() + ",loader:" + driver.getClass().getClassLoader());
        }
        System.out.println("current thread contxtloader:" + Thread.currentThread().getContextClassLoader());
        System.out.println("ServiceLoader loader:" + ServiceLoader.class.getClassLoader());
    }
}

執(zhí)行結(jié)果如下:

driver:class com.mysql.jdbc.Driver,loader:sun.misc.Launcher$AppClassLoader@2a139a55
driver:class com.mysql.fabric.jdbc.FabricMySQLDriver,loader:sun.misc.Launcher$AppClassLoader@2a139a55
driver:class com.alibaba.druid.proxy.DruidDriver,loader:sun.misc.Launcher$AppClassLoader@2a139a55
driver:class com.alibaba.druid.mock.MockDriver,loader:sun.misc.Launcher$AppClassLoader@2a139a55
current thread contxtloader:sun.misc.Launcher$AppClassLoader@2a139a55
ServiceLoader loader:null

從執(zhí)行結(jié)果中可以知道ServiceLoader的加載器為Bootstrap,因?yàn)檫@里輸出了null,并且從該類在rt.jar里面,也可以證明.

當(dāng)前線程上下文類加載器為AppClassLoader.而com.mysql.jdbc.Driver則使用AppClassLoader加載.我們知道如果一個(gè)類中引用了另外一個(gè)類,那么被引用的類也應(yīng)該由引用方類加載器來加載,而現(xiàn)在則是引用方ServiceLoader使用BootStartClassLoader加載,被引用方則使用子加載器APPClassLoader來加載了.是不是很詭異.

下面來看一下ServiceLoader的load方法源碼:

public static <S> ServiceLoader<S> load(Class<S> service,
                                        ClassLoader loader)
{
    return new ServiceLoader<>(service, loader);
}
public static <S> ServiceLoader<S> load(Class<S> service) {
    // 獲取當(dāng)前線程上下文加載器,這里是APPClassLoader
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

上述代碼獲得了線程上下文加載器(其實(shí)就是AppClassLoader),并將該類加載器傳遞到下面的ServiceLoader類的構(gòu)造方法loader成員變量中:

private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    reload();
}

上述loader變量什么時(shí)候使用的?看一下下面的代碼:

 public S next() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
        // 使用loader類加載器加載
        // 至于cn怎么來的,可以參照next()方法
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service,
             "Provider " + cn + " not found");
    }
    if (!service.isAssignableFrom(c)) {
        fail(service,
             "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
             x);
    }
    throw new Error();          // This cannot happen
}

到目前為止:ContextClassLoader的作用都是為了破壞Java類加載委托機(jī)制,JDBC規(guī)范定義了一個(gè)JDBC接口,然后使用SPI機(jī)制提供的一個(gè)叫做ServiceLoader的Java核心API(rt.jar里面提供)用來掃描服務(wù)實(shí)現(xiàn)類,服務(wù)實(shí)現(xiàn)者提供的jar,比如MySQL驅(qū)動則是放到我們的classpath下面.從上文知道默認(rèn)線程上下文類加載器就是AppClassLoader,所以例子里面沒有顯示在調(diào)用ServiceLoader前設(shè)置線程上下文類加載器為AppClassLoader,ServiceLoader內(nèi)部則獲取當(dāng)前線程上下文類加載器(這里為AppClassLoader)來加載服務(wù)實(shí)現(xiàn)者的類,這里加載了classpath下的MySQL的驅(qū)動實(shí)現(xiàn).

可以嘗試在調(diào)用ServiceLoader的load方法前設(shè)置線程上下文類加載器為ExtClassLoader,代碼如下:

Thread.currentThread().setContextClassLoader(ContextClassLoaderTest.class.getClassLoader().getParent());

然后運(yùn)行本例子,設(shè)置后ServiceLoader內(nèi)部則獲取當(dāng)前線程上下文類加載器為ExtClassLoader,然后會嘗試使用ExtClassLoader去查找JDBC驅(qū)動實(shí)現(xiàn),而ExtClassLoader掃描類的路徑為:JAVA_HOME/jre/lib/ext/,而這下面沒有驅(qū)動實(shí)現(xiàn)的Jar,所以不會查找到驅(qū)動.

總結(jié)下,當(dāng)父類加載器需要加載子類加載器中的資源時(shí),可以通過設(shè)置和獲取線程上下文類加載器來實(shí)現(xiàn).

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

相關(guān)文章

  • Java利用StringBuffer替換特殊字符的方法實(shí)現(xiàn)

    Java利用StringBuffer替換特殊字符的方法實(shí)現(xiàn)

    這篇文章主要介紹了Java利用StringBuffer替換特殊字符的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • java調(diào)用未知類的指定方法簡單實(shí)例

    java調(diào)用未知類的指定方法簡單實(shí)例

    這篇文章介紹了java調(diào)用未知類的指定方法簡單實(shí)例,有需要的朋友可以參考一下
    2013-09-09
  • 詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    圖文并茂的內(nèi)容往往讓人看起來更加舒服,如果只是文字內(nèi)容的累加,往往會使讀者產(chǎn)生視覺疲勞。搭配精美的文章配圖則會使文章內(nèi)容更加豐富。那我們要如何在PDF中插入、替換或刪除圖像呢?別擔(dān)心,今天為大家介紹一種高效便捷的方法
    2023-01-01
  • SpringAOP中@Pointcut的用法詳解

    SpringAOP中@Pointcut的用法詳解

    這篇文章主要介紹了SpringAOP中@Pointcut的用法詳解,Pointcut(切點(diǎn))是面向切面編程中的一個(gè)非常重要的概念,此概念由spring框架定義,Pointcut只是一種篩選規(guī)則,需要的朋友可以參考下
    2023-08-08
  • Java中的Valid和Validated的比較內(nèi)容

    Java中的Valid和Validated的比較內(nèi)容

    在本篇文章里小編給大家整理的是關(guān)于Java中的Valid和Validated的比較內(nèi)容,對此有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-02-02
  • SpringBoot嵌套事務(wù)詳解及失效解決方案

    SpringBoot嵌套事務(wù)詳解及失效解決方案

    在復(fù)雜的業(yè)務(wù)場景中,嵌套事務(wù)可以幫助我們更加精細(xì)地控制數(shù)據(jù)的一致性,然而,在 Spring Boot 中,如果嵌套事務(wù)的配置不當(dāng),可能會導(dǎo)致事務(wù)不生效的問題,尤其是在同一個(gè)類中進(jìn)行方法調(diào)用時(shí),本文將詳細(xì)介紹嵌套事務(wù)的原理、失效的原因以及解決方案
    2025-01-01
  • Servlet實(shí)現(xiàn)文件下載功能

    Servlet實(shí)現(xiàn)文件下載功能

    這篇文章主要為大家詳細(xì)介紹了Servlet實(shí)現(xiàn)文件下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • JVM解密之解構(gòu)類加載與GC垃圾回收機(jī)制詳解

    JVM解密之解構(gòu)類加載與GC垃圾回收機(jī)制詳解

    本文主要介紹了Java虛擬機(jī)(JVM)的內(nèi)存劃分、類加載機(jī)制以及垃圾回收機(jī)制,JVM的內(nèi)存劃分為程序計(jì)數(shù)器、棧、堆和方法區(qū),類加載機(jī)制包括類加載過程、加載器模型和雙親委派模型,垃圾回收機(jī)制主要包括標(biāo)記-清除、標(biāo)記-復(fù)制、標(biāo)記-整理和分代回收算法
    2025-02-02
  • Java 中限制方法的返回時(shí)間最新方法

    Java 中限制方法的返回時(shí)間最新方法

    最近在研究 ChatGPT 的 API 調(diào)用,因?yàn)?nbsp;ChatGPT 的 API 調(diào)用時(shí)間通常超過 30 秒,所以我們希望在程序中限制這個(gè)方法的執(zhí)行時(shí)間,不要讓方法花太長時(shí)間去執(zhí)行了,今天通過本文給大家分享Java 中如何限制方法的返回時(shí)間,感興趣的朋友跟隨小編一起看看吧
    2023-05-05
  • SpringBoot集成MyBatis的分頁插件PageHelper實(shí)例代碼

    SpringBoot集成MyBatis的分頁插件PageHelper實(shí)例代碼

    這篇文章主要介紹了SpringBoot集成MyBatis的分頁插件PageHelper的相關(guān)操作,需要的朋友可以參考下
    2017-08-08

最新評論