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

Java中的上下文加載器ContextClassLoader詳解

 更新時(shí)間:2023年10月07日 09:46:43   作者:邋遢的流浪劍客  
這篇文章主要介紹了Java中的上下文加載器ContextClassLoader詳解,ContextClassLoader是通過Thread.currentThread().getContextClassLoader()返回該線程上下文的ClassLoader,需要的朋友可以參考下

ContextClassLoader

ContextClassLoader是通過 Thread.currentThread().getContextClassLoader() 返回該線程上下文的ClassLoader

1、前置知識(shí)

在講解ContextClassLoader之前,需要先提兩個(gè)知識(shí)點(diǎn):

1)雙親委派模型

在這里插入圖片描述

  • 啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)將放在<JAVA HOME>\lib目錄中的,或者被-Xbootclasspath參數(shù)所指定的路徑中的,并且是虛擬機(jī)識(shí)別的類庫加載到虛擬機(jī)內(nèi)存中。啟動(dòng)類加載器無法被Java程序直接引用,用戶在編寫自定義類加載器時(shí),如果需要把加載請求委派給引導(dǎo)類加載器,那直接使用null代替即可
  • 擴(kuò)展類加載器(ExtClassLoader):由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載<JAVA HOME>\lib\ext目錄中的,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫,開發(fā)者可以直接使用擴(kuò)展類加載器
  • 應(yīng)用程序類加載器(AppClassLoader):由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)。由于這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱它為系統(tǒng)類加載。它負(fù)責(zé)加載用戶類路徑(ClassPath)上所有指定的類庫,開發(fā)者可以直接使用這個(gè)類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器

類加載之間的這種層次關(guān)系,稱為類加載器的雙親委派模型。雙親委派模型要求除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。這里類加載器之間的父子關(guān)系一般不會(huì)以繼承的關(guān)系來實(shí)現(xiàn),而是都使用組合關(guān)系來復(fù)用父加載器的代碼

雙親委派模型的工作過程:如果一個(gè)類加載器收到了類加載的請求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此,因此所有的加載請求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器,只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請求(它的搜索范圍中沒有找到所需的類)時(shí),子加載器才會(huì)嘗試自己去加載

使用雙親委派模型來組織類加載器之間的關(guān)系,有一個(gè)顯而易見的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。例如類 java.lang.Object ,它存放在 rt.jar 之中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的啟動(dòng)類加載器進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個(gè)類

2)如果一個(gè)類由類加載器A加載,那么這個(gè)類的依賴類也是由相同的類加載器加載

比如Spring作為一個(gè)Bean工廠,它需要?jiǎng)?chuàng)建業(yè)務(wù)類的實(shí)例,并且在創(chuàng)建業(yè)務(wù)類實(shí)例之前需要加載這些類。Spring是通過調(diào)用 Class.forName 來加載業(yè)務(wù)類的。調(diào)用 Class.forName() 的時(shí)候,會(huì)獲取調(diào)用該方法的類的類加載器,使用該類加載器來加載 Class.forName() 中傳入的類,代碼如下:

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
      	// 獲取調(diào)用該方法的類
        Class<?> caller = Reflection.getCallerClass();
      	// ClassLoader.getClassLoader獲取調(diào)用該方法的類的類加載器
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

2、為什么需要ContextClassLoader?

當(dāng)我們需要加載一個(gè)類,從自定義ClassLoader,到AppClassLoader,再到ExtClassLoader,最后到Bootstrap ClassLoader。沒問題, 很順利。這是從下到上加載。但是反過來,當(dāng)從上到下加載的時(shí)候,這個(gè)變得是一個(gè)不可能完成的任務(wù)。為了彌補(bǔ)這個(gè)缺陷, 特定設(shè)計(jì)的ContextClassLoader

這里你可能會(huì)有個(gè)疑問:為什么會(huì)出現(xiàn)從上到下加載的情況。比如一個(gè)類是由Bootstrap ClassLoader加載,該類引用了一個(gè)我們自己開發(fā)的類(該類能被AppClassLoader加載但不能被Bootstrap ClassLoader加載),由如果一個(gè)類由類加載器A加載,那么這個(gè)類的依賴類也是由相同的類加載器加載可知:默認(rèn)情況下我們自己開發(fā)的類會(huì)被Bootstrap ClassLoader嘗試加載,最終會(huì)由于無法加載到類而拋出異常

以SPI為例,SPI接口屬于Java核心庫,由BootstrapClassLoader加載,當(dāng)SPI接口想要引用第三方實(shí)現(xiàn)類的具體方法時(shí),BootstrapClassLoader無法加載Classpath下的第三方實(shí)現(xiàn)類,這時(shí)就需要使用線程上下文類加載器ContextClassLoader來解決。借助這種機(jī)制可以打破雙親委托機(jī)制限制

SPI核心類ServiceLoader源碼如下:

public final class ServiceLoader<S>
    implements Iterable<S>
{
    public static <S> ServiceLoader<S> load(Class<S> service) {
      	// 線程上下文類加載器,在Launcher類的構(gòu)造器中被賦值為AppClassLoader,它可以讀到ClassPath下的自定義類
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }     

在這里插入圖片描述

3、ContextClassLoader默認(rèn)為AppClassLoader

JVM啟動(dòng)時(shí),會(huì)去調(diào)用Launcher類的構(gòu)造方法:

public class Launcher {
    public Launcher() {
        ClassLoader extcl;
        try {
            // 首先創(chuàng)建擴(kuò)展類加載器
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                    "Could not create extension class loader");
        }
        // Now create the class loader to use to launch the application
        try {
            // 再創(chuàng)建AppClassLoader并把extcl作為父加載器傳遞給AppClassLoader
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                    "Could not create application class loader");
        }
        // 設(shè)置線程上下文類加載器,稍后分析
        Thread.currentThread().setContextClassLoader(loader);
        // 省略其他代碼...
    }

Launcher初始化時(shí)首先會(huì)創(chuàng)建ExtClassLoader類加載器,然后再創(chuàng)建AppClassLoader并把ExtClassLoader傳遞給它作為父類加載器,還把AppClassLoader默認(rèn)設(shè)置為線程上下文類加載器

4、子線程ContextClassLoader默認(rèn)為父線程的ContextClassLoader

Thread在 init() 方法中會(huì)把子線程ContextClassLoader設(shè)置為父線程的ContextClassLoader

public
class Thread implements Runnable {
    private ClassLoader contextClassLoader;
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        // 省略其他代碼...
      	// 當(dāng)前線程為父線程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */
            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }
            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();
        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        g.addUnstarted();
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
      	// 子線程ContextClassLoader設(shè)置為父線程的ContextClassLoader
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        // 省略其他代碼...
    }

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

相關(guān)文章

  • @FeignClient?path屬性路徑前綴帶路徑變量時(shí)報(bào)錯(cuò)的解決

    @FeignClient?path屬性路徑前綴帶路徑變量時(shí)報(bào)錯(cuò)的解決

    這篇文章主要介紹了@FeignClient?path屬性路徑前綴帶路徑變量時(shí)報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • springboot整合netty框架實(shí)現(xiàn)站內(nèi)信

    springboot整合netty框架實(shí)現(xiàn)站內(nèi)信

    Netty 是一個(gè)基于NIO的客戶、服務(wù)器端編程框架,使用Netty 可以確保你快速和簡單的開發(fā)出一個(gè)網(wǎng)絡(luò)應(yīng)用,這篇文章主要介紹了springboot整合netty框架的方式小結(jié),需要的朋友可以參考下
    2022-12-12
  • SpringBoot啟動(dòng)嵌入式Tomcat的實(shí)現(xiàn)步驟

    SpringBoot啟動(dòng)嵌入式Tomcat的實(shí)現(xiàn)步驟

    本文主要介紹了淺談SpringBoot如何啟動(dòng)嵌入式Tomcat,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Springboot 全局時(shí)間格式化三種方式示例詳解

    Springboot 全局時(shí)間格式化三種方式示例詳解

    時(shí)間格式化在項(xiàng)目中使用頻率是非常高的,當(dāng)我們的 API? 接口返回結(jié)果,需要對其中某一個(gè) date? 字段屬性進(jìn)行特殊的格式化處理,通常會(huì)用到 SimpleDateFormat? 工具處理,這篇文章主要介紹了3 種 Springboot 全局時(shí)間格式化方式,需要的朋友可以參考下
    2024-01-01
  • 使用Redis incr解決并發(fā)問題的操作

    使用Redis incr解決并發(fā)問題的操作

    這篇文章主要介紹了使用Redis incr解決并發(fā)問題的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • 深扒Java中POJO、VO、DO、DTO、PO、BO、AO、DAO的概念和區(qū)別以及如何應(yīng)用

    深扒Java中POJO、VO、DO、DTO、PO、BO、AO、DAO的概念和區(qū)別以及如何應(yīng)用

    po vo bo dto dao 和 pojo 是軟件開發(fā)中經(jīng)常使用的一些概念,用于設(shè)計(jì)和實(shí)現(xiàn)對象模型,下面將分別解釋這些概念的含義及其在開發(fā)中的應(yīng)用,這篇文章主要給大家介紹了關(guān)于Java中POJO、VO、DO、DTO、PO、BO、AO、DAO的概念和區(qū)別以及如何應(yīng)用的相關(guān)資料,需要的朋友可以參考下
    2024-08-08
  • 如何使用MAVEN打JAR包(直接使用)

    如何使用MAVEN打JAR包(直接使用)

    這篇文章主要介紹了如何使用MAVEN打JAR包(直接使用),文中通過實(shí)例代碼介紹了maven?使用assembly插件進(jìn)行打包的方法,需要的朋友可以參考下
    2023-03-03
  • SpringBoot整合MongoDB的步驟詳解

    SpringBoot整合MongoDB的步驟詳解

    這篇文章主要介紹了SpringBoot整合MongoDB的步驟詳解,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下
    2021-04-04
  • Maven打包JavaWeb項(xiàng)目的兩種實(shí)現(xiàn)方式

    Maven打包JavaWeb項(xiàng)目的兩種實(shí)現(xiàn)方式

    介紹了兩種Maven打包Web項(xiàng)目的方式:通過Eclipse和通過命令行,Eclipse方式包括清理、打包、跳過測試、輸入?Goals?等步驟,命令行方式包括進(jìn)入項(xiàng)目目錄、執(zhí)行?clean?和?package?命令、跳過測試等步驟,注意事項(xiàng)包括確保有JDK環(huán)境、正確配置pom.xml文件和修改版本號(hào)
    2025-02-02
  • SpringBoot的啟動(dòng)速度優(yōu)化

    SpringBoot的啟動(dòng)速度優(yōu)化

    隨著我們項(xiàng)目的不斷迭代 Bean 的數(shù)量會(huì)大大增加,如果都在啟動(dòng)時(shí)進(jìn)行初始化會(huì)非常耗時(shí),本文主要介紹了SpringBoot的啟動(dòng)速度優(yōu)化,感興趣的可以了解一下
    2023-09-09

最新評論