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

一篇文章講透Tomcat的類加載機制

 更新時間:2021年11月18日 11:19:38   作者:馬坤鵬 / 孫玄  
Tomcat的類加載機制是違反了雙親委托原則的,對于一些未加載的非基礎(chǔ)類,各個web應(yīng)用自己的類加載器會優(yōu)先加載,加載不到時再交給commonClassLoader走雙親委托,這篇文章主要給大家介紹了如何通過一篇文章講透Tomcat的類加載機制的相關(guān)資料,需要的朋友可以參考下

-     前言     -

你了解 Apache Tomcat 的類加載機制嗎?本文將從底層原理切入,徹底揭秘 Tomcat 類加載所涉及的源碼、機制和方案,助你深入掌握 Tomcat 類加載核心!

-     JVM 類加載器     -

1、JVM類加載器

說起 Tomcat 類加載器,就不得不先簡單說一下 JVM 類加載器,如下圖所示:

  • 啟動類加載器:Bootstrap ClassLoader,用于加載JVM提供的基礎(chǔ)運行類,即位于%JAVA_HOME%/jre/lib目錄下的核 心類庫;
  • 擴展類加載器:Extension ClassLoader, Java提供的一個標(biāo)準(zhǔn)的擴展機制用于加載除核心類庫外的Jar包,即只要復(fù)制 到指定的擴展目錄(可以多個)下的Jar, JVM會自動加載(不需要通過-classpath指定)。默認(rèn)的擴展目錄是%JAVA_HOME%加e/lib/ext。典型的應(yīng)用場景就是,Java使用該類加載 器加載JVM默認(rèn)提供的但是不屬于核心類庫的Jar。不推薦將應(yīng)用程序依賴的 類庫放置到擴展目錄下,因為該目錄下的類庫對所有基于該JVM運行的應(yīng)用程序可見;
  • 應(yīng)用程序類加載器:Application ClassLoader ,用于加載環(huán)境變量CLASSPATH (不推薦使用)指定目錄下的或者-classpath運行 參數(shù)指定的Jar包。System類加載器通常用于加載應(yīng)用程序Jar包及其啟動入口類(Tomcat 的Bootstrap類即由System類加載器加載)。

這些類加載器的工作原理是一樣的,區(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)自己的類加載邏輯。不知道你發(fā)現(xiàn)沒有,如果你要打破雙親委托機制,就需要重寫 loadClass 方法,因為 loadClass 的默認(rèn)實現(xiàn)就是雙親委托機制。

2、類加載器的源碼

public abstract class ClassLoader {
  //  每個類加載器都有一個父加載器
  private final ClassLoader parent;
  public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
     protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
           // 如果沒有加載過
            if (c == null) {
                if (parent != null) {
                  //  先委托給父加載器去加載,注意這是個遞歸調(diào)用
                 c = parent.loadClass(name, false);
                } else {
                 // 如果父加載器為空,查找 Bootstrap 加載器是不是加載過了
                   c = findBootstrapClassOrNull(name);
                }
              
            // 如果父加載器沒加載成功,調(diào)用自己的 findClass 去加載
                if (c == null) {        
                    c = findClass(name);
                }
            } 
        
            return c;
        }
        
    }
    //ClassLoader 中findClass方式需要被子類覆蓋,下面這段代碼就是對應(yīng)代碼
      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){
    
    }
    
}

我們自定義類加載器就需要重寫ClassLoader的loadClass方法。

-     Tomcat 的類加載機制     -

1、加載機制的特點

隔離性:Web應(yīng)用類庫相互隔離,避免依賴庫或者應(yīng)用包相互影響。設(shè)想一下,如果我們 有兩個Web應(yīng)用,一個釆用了Spring 2.5, 一個采用了Spring 4.0,而應(yīng)用服務(wù)器使用一個 類加載器加載,那么Web應(yīng)用將會由于Jar包覆蓋而導(dǎo)致無法啟動成功;

靈活性:既然Web應(yīng)用之間的類加載器相互獨立,那么我們就能只針對一個Web應(yīng)用進行 重新部署,此時該Web應(yīng)用的類加載器將會重新創(chuàng)建,而且不會影響其他Web應(yīng)用。如果 釆用一個類加載器,顯然無法實現(xiàn),因為只有一個類加載器的時候,類之間的依賴是雜 亂無章的,無法完整地移除某個Web應(yīng)用的類;

性能:由于每個Web應(yīng)用都有一個類加載器,因此Web應(yīng)用在加載類時,不會搜索其他 Web應(yīng)用包含的Jar包,性能自然高于應(yīng)用服務(wù)器只有一個類加載器的情況。

2、Tomcat 的類加載方案

  • 引導(dǎo)類加載器 和 擴展類加載器 的作⽤不變;
  • 系統(tǒng)類加載器正常情況下加載的是 CLASSPATH 下的類,但是 Tomcat 的啟動腳本并未使⽤該變量,⽽是加載tomcat啟動的類,⽐如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下;
  • Common 通⽤類加載器加載Tomcat使⽤以及應(yīng)⽤通⽤的⼀些類,位于CATALINA_HOME/lib下,⽐如servlet-api.jar;
  • Catalina ClassLoader ⽤于加載服務(wù)器內(nèi)部可⻅類,這些類應(yīng)⽤程序不能訪問;
  • SharedClassLoader ⽤于加載應(yīng)⽤程序共享類,這些類服務(wù)器不會依賴;
  • WebappClassLoader,每個應(yīng)⽤程序都會有⼀個獨⼀⽆⼆的Webapp ClassLoader,他⽤來加載本應(yīng)⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的類。

tomcat 8.5 默認(rèn)改變了嚴(yán)格的雙親委派機制:

  • 從緩存中加載;
  • 如果緩存中沒有,會先調(diào)用ExtClassLoader進行加載, 擴展類加載器是遵循雙親委派的,他會調(diào)用bootstrap,查看對應(yīng)的lib有沒有,然后回退給ExtClassLoader對擴展包下的數(shù)據(jù)進行加載;
  • 如果未加載到,則從 /WEB-INF/classes加載;
  • 如果未加載到,則從 /WEB-INF/lib/*.jar 加載如果未加載到,WebAppclassLoader 會委派給SharedClassLoader,SharedClassLoad會委派給CommonClassLoader.....,依次委派給BootstrapClassLoader, 然后BootstrapClassLoader 在自己目錄中查找對應(yīng)的類如果有則進行加載,如果沒有他會委派給下一級ExtClassLoader,ExtClassLoader再查找自己目錄下的類,如果有則加載如果沒有則委派給下一級……遵循雙親委派原則。

3、分析應(yīng)用類加載器的加載過程

應(yīng)用類加載器為WebappClassLoader ,他的loadClass在他的父類WebappClassLoaderBase中。

  public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            if (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Class<?> clazz = null;
            // Log access to stopped class loader
            checkStateForClassLoading(name);    
            //從當(dāng)前ClassLoader的本地緩存中加載類,如果找到則返回
            clazz = findLoadedClass0(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
            // 本地緩存沒有的情況下,調(diào)用ClassLoader的findLoadedClass方法查看jvm是否已經(jīng)加載過此類,如果已經(jīng)加載則直接返回。
            clazz = findLoadedClass(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
            String resourceName = binaryNameToPath(name, false);
            //此時的javaseClassLoader是擴展類加載器  是把擴展類加載器賦值給了javaseClassLoader
            ClassLoader javaseLoader = getJavaseClassLoader();
            boolean tryLoadingFromJavaseLoader;
            try {
              .....
            //如果可以用getResource得到
            //如果能用擴展類加載器的getResource得到就證明可以被擴展類加載器加載到接下來安排擴展類加載器加載
            if (tryLoadingFromJavaseLoader) {
                try {
                    //使用擴展類加載器進行加載
                    clazz = javaseLoader.loadClass(name);
                    if (clazz != null) {
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
            // (0.5) Permission to access this class when using a SecurityManager
            if (securityManager != null) {
                int i = name.lastIndexOf('.');
                if (i >= 0) {
                    try {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } catch (SecurityException se) {
                        String error = "Security Violation, attempt to use " +
                            "Restricted Class: " + name;
                        log.info(error, se);
                        throw new ClassNotFoundException(error, se);
                    }
                }
            }
            boolean delegateLoad = delegate || filter(name, true);
            // (1) Delegate to our parent if requested
            //如果是true就是用父類加載器進行加載
            if (delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader1 " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
            // (2) Search local repositories
            if (log.isDebugEnabled())
                log.debug("  Searching local repositories");
            try {
                // 本地進行加載
                clazz = findClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from local repository");
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
            // (3) Delegate to parent unconditionally
            //到這里還是沒有加載上再次嘗試使用父類加載器進行加載
            if (!delegateLoad) {
                    if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader at end: " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
        }
        throw new ClassNotFoundException(name);
    }

注:在37行英文注釋中標(biāo)注獲取的是系統(tǒng)類加載器,但我們debug的時候會發(fā)現(xiàn)他是擴展類加載器,實際中我們可以推斷出他應(yīng)該是擴展類加載器,因為如果我們加載的類在擴展類加載器路徑下已經(jīng)存在的話,那我們直接調(diào)用系統(tǒng)類加載器是就是錯誤的了,下圖為debug后獲取的類加載器的驗證。

總結(jié)

tomcat打破了雙親委派的原則,實際是在應(yīng)用類加載器中打破了雙親委派,其他類加載器還是遵循雙親委派的。

到此這篇關(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),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • tomcat訪問(access)日志配置和記錄Post請求參數(shù)

    tomcat訪問(access)日志配置和記錄Post請求參數(shù)

    這篇文章主要介紹了tomcat訪問(access)日志配置和記錄Post請求參數(shù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • Tomcat中catalina.out 和 catalina.log的區(qū)別和用途詳解

    Tomcat中catalina.out 和 catalina.log的區(qū)別和用途詳解

    本文主要介紹了Tomcat中catalina.out 和 catalina.log的區(qū)別和用途詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • HBuilderX配置tomcat外部服務(wù)器查看編輯jsp界面的方法詳解

    HBuilderX配置tomcat外部服務(wù)器查看編輯jsp界面的方法詳解

    這篇文章主要介紹了HBuilderX配置tomcat外部服務(wù)器查看編輯jsp界面的方法,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 直接雙擊啟動tomcat中的startup.bat閃退原因及解決方法

    直接雙擊啟動tomcat中的startup.bat閃退原因及解決方法

    免安裝的tomcat雙擊startup.bat后,啟動窗口一閃而過,而且tomcat服務(wù)未啟動,下面與大家分享下原因及解決方法
    2014-08-08
  • 基于Tomcat安全配置與性能優(yōu)化詳解

    基于Tomcat安全配置與性能優(yōu)化詳解

    下面小編就為大家分享一篇基于Tomcat安全配置與性能優(yōu)化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Tomcat使用線程池處理遠程并發(fā)請求的方法

    Tomcat使用線程池處理遠程并發(fā)請求的方法

    這篇文章主要介紹了Tomcat使用線程池處理遠程并發(fā)請求的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Tomcat服務(wù)配置和自啟動詳解

    Tomcat服務(wù)配置和自啟動詳解

    這篇文章主要為大家詳細(xì)介紹了Tomcat服務(wù)配置和自啟動的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Tomcat配置控制臺的實現(xiàn)

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

    本文主要介紹了Tomcat配置控制臺的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 在Windows10中安裝TomCat的教程圖解

    在Windows10中安裝TomCat的教程圖解

    Tomcat 服務(wù)器是一個免費的開放源代碼的Web 應(yīng)用服務(wù)器,屬于輕量級應(yīng)用服務(wù)器,在中小型系統(tǒng)和并發(fā)訪問用戶不是很多的場合下被普遍使用,是開發(fā)和調(diào)試JSP 程序的首選。這篇文章主要介紹了在Windows10中安裝TomCat的教程圖解,需要的朋友可以參考下
    2019-11-11

最新評論

青海省| 炉霍县| 岗巴县| 安阳市| 容城县| 定兴县| 莎车县| 中山市| 清丰县| 尚志市| 安达市| 乳源| 黄龙县| 伽师县| 遂平县| 阿拉善左旗| 玉树县| 滁州市| 休宁县| 天峨县| 平湖市| 琼海市| 敖汉旗| 茂名市| 黔东| 平阴县| 龙胜| 汉川市| 延边| 德清县| 三门峡市| 阳泉市| 海城市| 临邑县| 那坡县| 宜良县| 岐山县| 方正县| 绍兴县| 花垣县| 肃北|