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

淺談Tomcat如何打破雙親委托機制

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

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

要解決這個異常,你得知道

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

想想Tomcat又是如何加載和管理Web應用下的Servlet呢?
Tomcat正是通過Context組件來加載管理Web應用的,所以今天我會詳細分析Tomcat的類加載機制。但在這之前,我們有必要預習一下JVM的類加載機制,我會先回答一下一開始拋出來的問題,接著再談談Tomcat的類加載器如何打破Java的雙親委托機制。

JVM的類加載器

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

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

JVM并非在啟動時就把所有 .class 文件都加載一遍,而是程序在運行過程中用到該類才去加載。
JVM類加載由類加載器完成,JDK提供一個抽象類ClassLoader:

public abstract class ClassLoader {

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

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

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

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

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

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

Tomcat的類加載器

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

findClass

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

    return clazz;
}

工作流程

  • 先在Web應用本地目錄下查找要加載的類
  • 若未找到,交給父加載器查找,即AppClassLoader
  • 若父加載器也沒找到這個類,拋ClassNotFound

loadClass

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

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

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

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

        // 3. 嘗試用ExtClassLoader類加載器類加載,為什么?
        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)類加載器(也就是AppClassLoader)來加載
            try {
                clazz = Class.forName(name, false, parent);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
       }
    
    //6. 上述過程都加載失敗,拋出異常
    throw new ClassNotFoundException(name);
}

工作流程

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

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

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

相關(guān)文章

  • Tomcat starup.bat腳本開機自啟動的實現(xiàn)

    Tomcat starup.bat腳本開機自啟動的實現(xiàn)

    本文主要介紹了Tomcat starup.bat腳本開機自啟動的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • idea中Tomcat服務器啟動錯誤問題及解決

    idea中Tomcat服務器啟動錯誤問題及解決

    這篇文章主要介紹了idea中Tomcat服務器啟動錯誤問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Ubuntu安裝配置tomcat9教程

    Ubuntu安裝配置tomcat9教程

    這篇文章主要為大家詳細介紹了Ubuntu安裝配置tomcat9教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Tomcat配置控制臺的實現(xiàn)

    Tomcat配置控制臺的實現(xiàn)

    本文主要介紹了Tomcat配置控制臺的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • window7下Tomcat7.0安裝配置方法

    window7下Tomcat7.0安裝配置方法

    這篇文章主要介紹了window7下Tomcat7.0安裝配置方法,需要的朋友可以參考下
    2014-07-07
  • Tomcat安裝與配置Native APR模式的教程

    Tomcat安裝與配置Native APR模式的教程

    APR是Apache HTTP服務器的支持庫,提供了一組映射到下層操作系統(tǒng)的API。通過 tomcat-native 庫,使tomcat運行時通過APR更多的調(diào)用本地API,達到提升性能的目的。下面這篇文章詳細介紹了Tomcat安裝與配置Native APR模式的教程,需要的朋友可以參考借鑒,一起來看看吧。
    2017-02-02
  • Tomcat服務器的配置與啟動全過程

    Tomcat服務器的配置與啟動全過程

    這篇文章主要介紹了Tomcat服務器的配置與啟動全過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • startup.bat啟動Tomcat閃退問題原因及解決

    startup.bat啟動Tomcat閃退問題原因及解決

    本文主要介紹了startup.bat啟動Tomcat閃退問題原因及解決,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • ELK收集Tomcat日志的實現(xiàn)

    ELK收集Tomcat日志的實現(xiàn)

    本文主要介紹了ELK收集Tomcat日志的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • tomcat 騰訊云主機和微信

    tomcat 騰訊云主機和微信

    本文主要介紹tomcat 騰訊云主機和微信,這里整理了詳細的資料及實現(xiàn)步驟,有興趣的小伙伴可以參考下
    2016-09-09

最新評論