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

Java類加載器之ContextClassLoader詳解

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

ContextClassLoader

ContextClassLoader是一種與線程相關(guān)的類加載器,類似ThreadLocal,每個(gè)線程對(duì)應(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)前線程上下文類加載器為老的類加載器

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

具體來說,比如Java中的SPI(Service Provider Interface)是面向接口編程的,服務(wù)規(guī)則提供者會(huì)在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ū)動(dòng)提供商提供的實(shí)現(xiàn)類,提供者只需在JDBC實(shí)現(xiàn)的jar的META-INF/services/java.sql.Driver文件里指定實(shí)現(xiàn)類的方式暴露驅(qū)動(dòng)提供者.例如:MYSQL實(shí)現(xiàn)的jar如下:

這里寫圖片描述

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

public class Driver extends NonRegisteringDriver implements java.sql.Driver

引入MySQL驅(qū)動(dòng)的jar包,測(cè)試類如下:

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ū)動(dòng)則是放到我們的classpath下面.從上文知道默認(rèn)線程上下文類加載器就是AppClassLoader,所以例子里面沒有顯示在調(diào)用ServiceLoader前設(shè)置線程上下文類加載器為AppClassLoader,ServiceLoader內(nèi)部則獲取當(dāng)前線程上下文類加載器(這里為AppClassLoader)來加載服務(wù)實(shí)現(xiàn)者的類,這里加載了classpath下的MySQL的驅(qū)動(dòng)實(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,然后會(huì)嘗試使用ExtClassLoader去查找JDBC驅(qū)動(dòng)實(shí)現(xiàn),而ExtClassLoader掃描類的路徑為:JAVA_HOME/jre/lib/ext/,而這下面沒有驅(qū)動(dòng)實(shí)現(xiàn)的Jar,所以不會(huì)查找到驅(qū)動(dòng).

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

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

相關(guān)文章

  • Spring中配置ContextLoaderListener方式

    Spring中配置ContextLoaderListener方式

    這篇文章主要介紹了Spring中配置ContextLoaderListener方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Session和JWT的區(qū)別是什么以及與Cookie相比詳解

    Session和JWT的區(qū)別是什么以及與Cookie相比詳解

    Web應(yīng)用需客戶端存儲(chǔ)用戶信息,Cookie是早期方案,有特點(diǎn)及驗(yàn)證方式,下面這篇文章主要介紹了Session和JWT的區(qū)別是什么以及與Cookie相比的相關(guān)資料,文中通過代碼就介紹的非常詳細(xì),需要的朋友可以參考下
    2026-04-04
  • JVM中的垃圾回收器使用及說明

    JVM中的垃圾回收器使用及說明

    文章介紹了Java垃圾回收器的類型、工作流程以及內(nèi)存分配和回收策略,垃圾回收器包括Serial、ParNew、ParallelScavenge、SerialOld、ParallelOld、CMS和G1等,分為新生代和老年代回收,并且對(duì)象優(yōu)先在Eden區(qū)分配,大對(duì)象直接進(jìn)入老年代,長期存活的對(duì)象晉升到老年代
    2025-12-12
  • Java中實(shí)現(xiàn)二叉樹的遍歷與重構(gòu)

    Java中實(shí)現(xiàn)二叉樹的遍歷與重構(gòu)

    這篇文章主要介紹了Java中實(shí)現(xiàn)二叉樹的遍歷與重構(gòu),樹是一種非線性的數(shù)據(jù)結(jié)構(gòu),它是由n(n>=0)個(gè)有限結(jié)點(diǎn)組成一個(gè)具有層次關(guān)系的集合,把它叫做樹是因?yàn)樗雌饋硐褚豢玫箳斓臉?也就是說它是根朝上,而葉朝下的,需要的朋友可以參考下
    2023-10-10
  • SpringCloud Gateway實(shí)現(xiàn)限流功能詳解

    SpringCloud Gateway實(shí)現(xiàn)限流功能詳解

    SpringCloud Gateway 是 Spring Cloud 的一個(gè)全新項(xiàng)目,它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的 API 路由管理方式。這篇文章主要介紹了SpringCloud Gateway實(shí)現(xiàn)限流,需要的朋友可以參考下
    2022-11-11
  • springboot整合sentinel接口熔斷的實(shí)現(xiàn)示例

    springboot整合sentinel接口熔斷的實(shí)現(xiàn)示例

    為了防止慢接口導(dǎo)致的服務(wù)阻塞,可以通過添加熔斷處理來避免應(yīng)用的大量工作線程陷入阻塞,保證其他接口的正常運(yùn)行,本文介紹了如何使用Spring Boot與Sentinel進(jìn)行接口熔斷的配置與實(shí)現(xiàn),感興趣的可以了解一下
    2024-09-09
  • Java 集合實(shí)現(xiàn)分頁的方法(業(yè)務(wù)代碼實(shí)現(xiàn)分頁)

    Java 集合實(shí)現(xiàn)分頁的方法(業(yè)務(wù)代碼實(shí)現(xiàn)分頁)

    在Java開發(fā)中,有些場(chǎng)景比較復(fù)雜,受限制,不好在sql查詢層面實(shí)現(xiàn)分頁,需要在查詢的list結(jié)果后,將list分頁返回,如何實(shí)現(xiàn)呢,帶著這個(gè)問題一起通過本文學(xué)習(xí)吧
    2025-02-02
  • Spring基于@Conditional條件化裝配bean

    Spring基于@Conditional條件化裝配bean

    這篇文章主要介紹了Spring @Conditional條件化裝配bean,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java異常處理中的一些特殊情況舉例

    Java異常處理中的一些特殊情況舉例

    這篇文章主要介紹了Java異常處理中的一些特殊情況舉例,分別是只用try和finally不用catch,以及finally語句不被執(zhí)行的情況,需要的朋友可以參考下
    2015-11-11
  • Java如何解析html中的內(nèi)容并存到數(shù)據(jù)庫詳解

    Java如何解析html中的內(nèi)容并存到數(shù)據(jù)庫詳解

    最近用到了Java解析Html的一個(gè)庫Jsoup,所以下面這篇文章主要給大家介紹了關(guān)于Java如何解析html中的內(nèi)容并存到數(shù)據(jù)庫的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03

最新評(píng)論

尉氏县| 开阳县| 马关县| 宁城县| 阿拉善左旗| 通化市| 如皋市| 婺源县| 太仆寺旗| 福贡县| 六盘水市| 焉耆| 枝江市| 福鼎市| 高尔夫| 托里县| 烟台市| 铜梁县| 社旗县| 偃师市| 大余县| 南江县| 平顺县| 甘泉县| 白沙| 剑川县| 启东市| 西华县| 青河县| 乌兰县| 洱源县| 伊川县| 东丰县| 汽车| 天祝| 遂平县| 临潭县| 德江县| 普兰店市| 准格尔旗| 商城县|