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

淺談Tomcat如何打破雙親委托機(jī)制

 更新時(shí)間:2021年07月30日 11:41:58   作者:JavaEdge.  
本文主要介紹了淺談Tomcat如何打破雙親委托機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

我們經(jīng)常會(huì)遇到ClassNotFound異常,表明JVM在嘗試加載某類(lèi)時(shí)失敗了。

要解決這個(gè)異常,你得知道

  • 什么是類(lèi)加載
  • JVM如何加載類(lèi)
  • 為什么會(huì)出現(xiàn)ClassNotFound

想想Tomcat又是如何加載和管理Web應(yīng)用下的Servlet呢?
Tomcat正是通過(guò)Context組件來(lái)加載管理Web應(yīng)用的,所以今天我會(huì)詳細(xì)分析Tomcat的類(lèi)加載機(jī)制。但在這之前,我們有必要預(yù)習(xí)一下JVM的類(lèi)加載機(jī)制,我會(huì)先回答一下一開(kāi)始拋出來(lái)的問(wèn)題,接著再談?wù)凾omcat的類(lèi)加載器如何打破Java的雙親委托機(jī)制。

JVM的類(lèi)加載器

Java的類(lèi)加載,就是把字節(jié)碼格式.class文件加載到JVM的方法區(qū),并在JVM堆建立一個(gè)java.lang.Class對(duì)象實(shí)例,封裝Java類(lèi)相關(guān)的數(shù)據(jù)和方法。

Class對(duì)象是什么?
可以理解成業(yè)務(wù)類(lèi)的模板,JVM根據(jù)該模板創(chuàng)建具體業(yè)務(wù)類(lèi)對(duì)象實(shí)例。

JVM并非在啟動(dòng)時(shí)就把所有 .class 文件都加載一遍,而是程序在運(yùn)行過(guò)程中用到該類(lèi)才去加載。
JVM類(lèi)加載由類(lèi)加載器完成,JDK提供一個(gè)抽象類(lèi)ClassLoader:

public abstract class ClassLoader {

    // 每個(gè)類(lèi)加載器都有個(gè)父加載器
    private final ClassLoader parent;
    
    public Class<?> loadClass(String name) {
  
        // 查找該類(lèi)是否被加載過(guò)
        Class<?> c = findLoadedClass(name);
        
        // 若未被加載過(guò)
        if( c == null ){
          // 【遞歸】委托給父加載器加載
          if (parent != null) {
              c = parent.loadClass(name);
          } else {
              // 若父加載器為空,查找Bootstrap加載器是否加載過(guò)了
              c = findBootstrapClassOrNull(name);
          }
        }
        // 若父加載器未加載成功,調(diào)用自己的findClass去加載
        if (c == null) {
            c = findClass(name);
        }
        
        return c;
    }
    
    protected Class<?> findClass(String name){
       // 1. 根據(jù)傳入的類(lèi)名name,到在特定目錄下去尋找類(lèi)文件,把.class文件讀入內(nèi)存
          ...
          
       // 2. 調(diào)用defineClass將字節(jié)數(shù)組轉(zhuǎn)成Class對(duì)象
       return defineClass(buf, off, len);
    }
    
    // 將字節(jié)碼數(shù)組解析成一個(gè)Class對(duì)象,用native方法實(shí)現(xiàn)
    protected final Class<?> defineClass(byte[] b, int off, int len){
       ...
    }
}

JVM的類(lèi)加載器是分層的父子關(guān)系,每個(gè)類(lèi)加載器都持有一個(gè)parent字段指向父加載器。

  • defineClass 工具方法:調(diào)用native方法把Java類(lèi)的字節(jié)碼解析成一個(gè)Class對(duì)象
  • findClass 就是找到 .class 文件,可能來(lái)自文件系統(tǒng)或網(wǎng)絡(luò),找到后把 .class 文件讀到內(nèi)存得到字節(jié)碼數(shù)組,然后調(diào)用defineClass方法得到Class對(duì)象

loadClass 首先檢查這個(gè)類(lèi)是不是已經(jīng)被加載過(guò)了,如果加載過(guò)了直接返回,否則交給父加載器去加載。
這是個(gè)遞歸調(diào)用,即子加載器持有父加載器引用,當(dāng)一個(gè)類(lèi)加載器需加載一個(gè)Java類(lèi)時(shí),會(huì)先委托父加載器去加載,然后父加載器在自己加載路徑中搜索Java類(lèi),當(dāng)父加載器在自己的加載范圍內(nèi)找不到時(shí),才會(huì)交還給子加載器加載,這就是雙親委托機(jī)制。

JDK的類(lèi)加載器工作原理是一樣的,區(qū)別只是加載路徑不同,即findClass查找的路徑不同。
雙親委托機(jī)制是為保證一個(gè)Java類(lèi)在JVM的唯一性。假如你手滑寫(xiě)個(gè)與JRE核心類(lèi)同名類(lèi),比如Object,雙親委托機(jī)制能保證加載的是JRE里的那個(gè)Object類(lèi),而不是你寫(xiě)的Object。
因?yàn)锳ppClassLoader在加載你的Object類(lèi)時(shí),會(huì)委托給ExtClassLoader去加載,而ExtClassLoader又會(huì)委托給BootstrapClassLoader,BootstrapClassLoader發(fā)現(xiàn)自己已經(jīng)加載過(guò)了Object類(lèi),會(huì)直接返回,不會(huì)去加載你的Object類(lèi)。

類(lèi)加載器的父子關(guān)系不是通過(guò)繼承來(lái)實(shí)現(xiàn)的,比如AppClassLoader并非ExtClassLoader的子類(lèi),只是AppClassLoader的parent指向ExtClassLoader對(duì)象。
所以若自定義類(lèi)加載器,不是去繼承AppClassLoader,而是繼承ClassLoader抽象類(lèi),再重寫(xiě)findClass和loadClass即可。
Tomcat就是通過(guò)自定義類(lèi)加載器實(shí)現(xiàn)自己的類(lèi)加載。
若你要打破雙親委托,也就只需重寫(xiě)loadClass,因?yàn)閘oadClass的默認(rèn)實(shí)現(xiàn)就是雙親委托機(jī)制。

Tomcat的類(lèi)加載器

Tomcat的自定義類(lèi)加載器WebAppClassLoader打破了雙親委托機(jī)制:
首先自己嘗試去加載某個(gè)類(lèi),如果找不到再委托給父類(lèi)加載器,目的是優(yōu)先加載Web應(yīng)用自己定義的類(lèi)。
只需重寫(xiě)ClassLoader的兩個(gè)方法:

findClass

public Class<?> findClass(String name) throws ClassNotFoundException {
    ...
    
    Class<?> clazz = null;
    try {
            //1. 先在Web應(yīng)用目錄下查找類(lèi) 
            clazz = findClassInternal(name);
    }  catch (RuntimeException e) {
           throw e;
       }
    
    if (clazz == null) {
    try {
            //2. 如果在本地目錄沒(méi)有找到,交給父加載器去查找
            clazz = super.findClass(name);
    }  catch (RuntimeException e) {
           throw e;
       }
    
    //3. 如果父類(lèi)也沒(méi)找到,拋出ClassNotFoundException
    if (clazz == null) {
        throw new ClassNotFoundException(name);
     }

    return clazz;
}

工作流程

  • 先在Web應(yīng)用本地目錄下查找要加載的類(lèi)
  • 若未找到,交給父加載器查找,即AppClassLoader
  • 若父加載器也沒(méi)找到這個(gè)類(lèi),拋ClassNotFound

loadClass

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

    synchronized (getClassLoadingLock(name)) {
 
        Class<?> clazz = null;

        //1. 先在本地cache查找該類(lèi)是否已經(jīng)加載過(guò)
        clazz = findLoadedClass0(name);
        if (clazz != null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        //2. 從系統(tǒng)類(lèi)加載器的cache中查找是否加載過(guò)
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        // 3. 嘗試用ExtClassLoader類(lèi)加載器類(lèi)加載,為什么?
        ClassLoader javaseLoader = getJavaseClassLoader();
        try {
            clazz = javaseLoader.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

        // 4. 嘗試在本地目錄搜索class并加載
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

        // 5. 嘗試用系統(tǒng)類(lèi)加載器(也就是AppClassLoader)來(lái)加載
            try {
                clazz = Class.forName(name, false, parent);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
       }
    
    //6. 上述過(guò)程都加載失敗,拋出異常
    throw new ClassNotFoundException(name);
}

工作流程

  • 先在本地Cache查找該類(lèi)是否已加載過(guò)
  • 即Tomcat的類(lèi)加載器是否已經(jīng)加載過(guò)這個(gè)類(lèi)。
  • 若Tomcat類(lèi)加載器尚未加載過(guò)該類(lèi),再看看系統(tǒng)類(lèi)加載器是否加載過(guò)
  • 若都沒(méi)有,就讓ExtClassLoader加載,為防止Web應(yīng)用自己的類(lèi)覆蓋JRE的核心類(lèi)
  • 因?yàn)門(mén)omcat需打破雙親委托,假如Web應(yīng)用里自定義了一個(gè)叫Object的類(lèi),若先加載該Object類(lèi),就會(huì)覆蓋JRE的Object類(lèi),所以Tomcat類(lèi)加載器優(yōu)先嘗試用ExtClassLoader去加載,因?yàn)镋xtClassLoader會(huì)委托給BootstrapClassLoader去加載,BootstrapClassLoader發(fā)現(xiàn)自己已經(jīng)加載了Object類(lèi),直接返回給Tomcat的類(lèi)加載器,這樣Tomcat的類(lèi)加載器就不會(huì)去加載Web應(yīng)用下的Object類(lèi)了,避免覆蓋JRE核心類(lèi)。
  • 若ExtClassLoader加載失敗,即JRE無(wú)此類(lèi),則在本地Web應(yīng)用目錄下查找并加載
  • 若本地目錄下無(wú)此類(lèi),說(shuō)明不是Web應(yīng)用自己定義的類(lèi),那么由系統(tǒng)類(lèi)加載器去加載。這里請(qǐng)你注意,Web應(yīng)用是通過(guò)Class.forName調(diào)用交給系統(tǒng)類(lèi)加載器的,因?yàn)镃lass.forName的默認(rèn)加載器就是系統(tǒng)類(lèi)加載器。
  • 若上述加載過(guò)程都失敗,拋ClassNotFound

可見(jiàn) Tomcat 類(lèi)加載器打破了雙親委托,沒(méi)有一上來(lái)就直接委托給父加載器,而是先在本地目錄下加載。
但為避免本地目錄類(lèi)覆蓋JRE核心類(lèi),會(huì)先嘗試用ExtClassLoader加載。
那為何不先用AppClassLoader加載?
若這樣,就又變成雙親委托,這就是Tomcat類(lèi)加載器的奧妙。

到此這篇關(guān)于淺談Tomcat如何打破雙親委托機(jī)制的文章就介紹到這了,更多相關(guān)Tomcat 雙親委托機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談Tomcat如何打破雙親委托機(jī)制

    淺談Tomcat如何打破雙親委托機(jī)制

    本文主要介紹了淺談Tomcat如何打破雙親委托機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 詳解Tomcat常用的過(guò)濾器

    詳解Tomcat常用的過(guò)濾器

    過(guò)濾器Filter是定義于tomcat的servlet-api.jar中的一個(gè)接口,接口路徑為javax.servlet.Filter。tomcat過(guò)濾器采用了典型的過(guò)濾器設(shè)計(jì)模式,過(guò)濾器鏈FilterChain由tomcat維持,鏈條是可以支持多個(gè)過(guò)濾器的
    2021-06-06
  • 如何通過(guò)LambdaProbe實(shí)現(xiàn)監(jiān)控Tomcat

    如何通過(guò)LambdaProbe實(shí)現(xiàn)監(jiān)控Tomcat

    這篇文章主要介紹了如何通過(guò)LambdaProbe實(shí)現(xiàn)監(jiān)控Tomcat,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Tomcat開(kāi)啟JMX服務(wù)方法介紹

    Tomcat開(kāi)啟JMX服務(wù)方法介紹

    這篇文章主要介紹了Tomcat開(kāi)啟JMX服務(wù)方法的相關(guān)內(nèi)容,小編覺(jué)得挺不錯(cuò)的,在這里給大家分享一下,需要的朋友可以參考。
    2017-10-10
  • Tomcat在linux環(huán)境中開(kāi)機(jī)自啟(定時(shí)重啟)的方法

    Tomcat在linux環(huán)境中開(kāi)機(jī)自啟(定時(shí)重啟)的方法

    我們經(jīng)常會(huì)遇到服務(wù)器斷電或異常,而異常后tomcat中部署的web項(xiàng)目需要我手動(dòng)去啟動(dòng),為此,特別貢獻(xiàn)出Linux環(huán)境中Tomcat開(kāi)機(jī)自啟的方式供學(xué)習(xí)使用,需要的朋友可以參考下
    2023-10-10
  • 詳解將Web項(xiàng)目War包部署到Tomcat服務(wù)器基本步驟

    詳解將Web項(xiàng)目War包部署到Tomcat服務(wù)器基本步驟

    本篇文章主要介紹了詳解將Web項(xiàng)目War包部署到Tomcat服務(wù)器基本步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Tomcat8使用cronolog來(lái)進(jìn)行Catalina.Out日志分割方法

    Tomcat8使用cronolog來(lái)進(jìn)行Catalina.Out日志分割方法

    今天通過(guò)本文給大家分享如何使用cronolog來(lái)分割catalina.out日志文件,對(duì)Tomcat Catalina.Out日志分割方法感興趣的朋友跟隨小編一起看看吧
    2021-09-09
  • 淺談Tomcat多層容器的設(shè)計(jì)

    淺談Tomcat多層容器的設(shè)計(jì)

    Tomcat的容器用來(lái)裝載Servlet。那Tomcat的Servlet容器是如何設(shè)計(jì)的呢,本文就詳細(xì)的來(lái)介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-07-07
  • 淺談Tomcat內(nèi)存配置的正確姿勢(shì)

    淺談Tomcat內(nèi)存配置的正確姿勢(shì)

    這篇文章主要介紹了淺談Tomcat內(nèi)存配置的正確姿勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Tomcat服務(wù)器的啟動(dòng)及啟動(dòng)失敗可能的原因分析

    Tomcat服務(wù)器的啟動(dòng)及啟動(dòng)失敗可能的原因分析

    這篇文章主要介紹了Tomcat服務(wù)器的啟動(dòng)及啟動(dòng)失敗可能的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評(píng)論

繁峙县| 宁安市| 襄樊市| 江达县| 镇赉县| 河北区| 临潭县| 化州市| 莒南县| 龙南县| 巴彦县| 伊川县| 刚察县| 丰城市| 西城区| 长汀县| 龙海市| 定安县| 临泽县| 陈巴尔虎旗| 吐鲁番市| 崇州市| SHOW| 峡江县| 江陵县| 亚东县| 德惠市| 油尖旺区| 平和县| 郯城县| 北安市| 滨海县| 虎林市| 赤城县| 广安市| 珠海市| 绥芬河市| 鄯善县| 彰化市| 绵竹市| 大冶市|