中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Tomcat中的類加載器怎么用

發布時間:2021-10-19 17:40:55 來源:億速云 閱讀:141 作者:柒染 欄目:大數據

Tomcat中的類加載器怎么用,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

Tomcat中的類加載器

在學習Tomcat中的類加載器,并且Tomcat為什么要實現自己的類加載器打破雙親委派模型原因之前,我們首先需要知道Java中定義的類加載器是什么,雙親委派模型是什么。

Java中的類加載器

類加載器負責在程序運行時將java文件動態加載到JVM中

從Java虛擬機的角度來講的話,存在兩種不同的類加載器:

  • 啟動類加載器(Bootstrap ClassLoader):這個類加載器是使用C++語言實現的,是虛擬機自身的一部分。

  • 其他的類加載器:這些類加載器都由Java語言實現,獨立于虛擬機外部,并且全都繼承自抽象類java.lang.ClassLoader,其中其他類加載器大概又分為

    • ExtensionClassLoader:這個類加載器由ExtClassLoader實現,它負責加載JAVA_HOME/lib/ext目錄中的所有類,或者被java.ext.dir系統變量所指定的路徑中所有的類。

    • ApplicationClassLoader:這個類加載器是由AppClassLoader實現的,它負責加載用戶類路徑(ClassPath)上所指定的所有類,如果應用中沒有自定義自己的類加載器,那么一般情況就是程序中默認的類加載器。

    • 自定義加載器:根據自己需求,自定義加載特定路徑的加載器。

Tomcat中的類加載器怎么用

對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性

雙親委派模型

上圖中展示的層次結構,稱之為類加載器的雙親委派模型。雙親委派模型要求除了頂層的啟動類加載器外,其他加載器都應該有自己的父加載器。這里的父子關系不是通過繼承來實現的,而是通過設置parent變量來實現的。

雙親委派模型工作過程是:如果收到一個類加載的請求,本身不會先加載此類,而是會先將此請求委派給父類加載器去完成,每個層次都是如此,直到啟動類加載器中,只有父類都沒有加載此文件,那么子類才會嘗試自己去加載。

為什么要設置雙親委派模型呢?其實是為了保證Java程序的穩定運行,例如Object類,它是存放在rt.jar中,無論哪一個類加載器要加載Object類,最終都會委托給頂層的BootStrapClassLoader,所以所有的類中使用的Object都是同一個類,相反如果沒有雙親委派模型的話,那么隨意一個類加載器都可以定義一個新的Object類,那么應用程序將會變得非常混亂。其實雙親委派模型代碼非常簡單。實現在ClassLoader中的loadClass方法下。

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先,檢查請求類是否被加載過
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
            // 如果沒被本類類加載器加載過,先委托給父類進行加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                // 如果沒有父類,則表明在頂層,就交給BootStrap類加載器加載
                    c = findBootstrapClassOrNull(name);
                }
                // 如果最頂層的類也找不到,那么就會拋出ClassNotFoundException異常
            } catch (ClassNotFoundException e) {

            }
            // 如果父類都沒有加載過此類,子類才開始加載此類
            if (c == null) {
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

我們可以看到findClass方法是需要子類自己去實現的邏輯。

Tomcat中的類加載器

下面的簡圖是Tomcat9版本的官方文檔給出的Tomcat的類加載器的圖。

  Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ..
  • Bootstrap : 是Java的最高的加載器,用C語言實現,主要用來加載JVM啟動時所需要的核心類,例如$JAVA_HOME/jre/lib/ext路徑下的類。

  • System: 會加載CLASSPATH系統變量所定義路徑的所有的類。

  • Common:會加載Tomcat路徑下的lib文件下的所有類。

  • Webapp1、Webapp2……: 會加載webapp路徑下項目中的所有的類。一個項目對應一個WebappClassLoader,這樣就實現了應用之間類的隔離了。

這3個部分,在上面的Java雙親委派模型圖中都有體現。不過可以看到ExtClassLoader沒有畫出來,可以理解為是跟bootstrap合并了,都是去JAVA_HOME/jre/lib下面加載類。 那么Tomcat為什么要自定義類加載器呢?

  • 隔離不同應用:部署在同一個Tomcat中的不同應用A和B,例如A用了Spring2.5。B用了Spring3.5,那么這兩個應用如果使用的是同一個類加載器,那么Web應用就會因為jar包覆蓋而無法啟動。

  • 靈活性:Web應用之間的類加載器相互獨立,那么就可以根據修改不同的文件重建不同的類加載器替換原來的。從而不影響其他應用。

  • 性能:如果在一個Tomcat部署多個應用,多個應用中都有相同的類庫依賴。那么可以把這相同的類庫讓Common類加載器進行加載。

Tomcat自定義了WebAppClassLoader類加載器。打破了雙親委派的機制,即如果收到類加載的請求,會嘗試自己去加載,如果找不到再交給父加載器去加載,目的就是為了優先加載Web應用自己定義的類。我們知道ClassLoader默認的loadClass方法是以雙親委派的模型進行加載類的,那么Tomcat既然要打破這個規則,就要重寫loadClass方法,我們可以看WebAppClassLoader類中重寫的loadClass方法。

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

    synchronized (getClassLoadingLock(name)) {
        Class<?> clazz = null;
        // 1. 從本地緩存中查找是否加載過此類
        clazz = findLoadedClass0(name);
        if (clazz != null) {
            if (log.isDebugEnabled())
                log.debug("  Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        // 2. 從AppClassLoader中查找是否加載過此類
        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);
        // 3. 嘗試用ExtClassLoader 類加載器加載類,防止Web應用覆蓋JRE的核心類
        ClassLoader javaseLoader = getJavaseClassLoader();
        boolean tryLoadingFromJavaseLoader;
        try {
            URL url;
            if (securityManager != null) {
                PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
                url = AccessController.doPrivileged(dp);
            } else {
                url = javaseLoader.getResource(resourceName);
            }
            tryLoadingFromJavaseLoader = (url != null);
        } catch (Throwable t) {
            tryLoadingFromJavaseLoader = true;
        }

        boolean delegateLoad = delegate || filter(name, true);

        // 4. 判斷是否設置了delegate屬性,如果設置為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
            }
        }

        // 5. 默認是設置delegate是false的,那么就會先用WebAppClassLoader進行加載
        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
        }

        // 6. 如果此時在WebAppClassLoader沒找到類,那么就委托給AppClassLoader去加載
        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);
}

最后借用Tomcat官網上的話總結:

Web應用默認的類加載順序是(打破了雙親委派規則):

  1. 先從JVM的BootStrapClassLoader中加載。

  2. 加載Web應用下/WEB-INF/classes中的類。

  3. 加載Web應用下/WEB-INF/lib/*.jap中的jar包中的類。

  4. 加載上面定義的System路徑下面的類。

  5. 加載上面定義的Common路徑下面的類。

如果在配置文件中配置了<Loader delegate="true"/>,那么就是遵循雙親委派規則,加載順序如下:

  1. 先從JVM的BootStrapClassLoader中加載。

  2. 加載上面定義的System路徑下面的類。

  3. 加載上面定義的Common路徑下面的類。

  4. 加載Web應用下/WEB-INF/classes中的類。

  5. 加載Web應用下/WEB-INF/lib/*.jap中的jar包中的類。

看完上述內容,你們掌握Tomcat中的類加載器怎么用的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

济阳县| 黔南| 荆门市| 信丰县| 锡林浩特市| 太康县| 乌恰县| 高唐县| 江达县| 夏邑县| 宜良县| 二连浩特市| 江安县| 麦盖提县| 金门县| 河南省| 平江县| 冕宁县| 高陵县| 平遥县| 普洱| 开鲁县| 浦城县| 黑河市| 霞浦县| 北京市| 酉阳| 炉霍县| 阜阳市| 乌兰浩特市| 特克斯县| 阆中市| 清徐县| 西林县| 城步| 卢氏县| 新和县| 靖安县| 平武县| 诏安县| 峡江县|