国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

一篇文章講透Tomcat的類(lèi)加載機(jī)制

瀏覽:114日期:2023-03-19 16:51:41
目錄
  • - 前言 -
  • - JVM 類(lèi)加載器 -
    • 1、JVM類(lèi)加載器
    • 2、類(lèi)加載器的源碼
  • - Tomcat 的類(lèi)加載機(jī)制 -
    • 1、加載機(jī)制的特點(diǎn)
    • 2、Tomcat 的類(lèi)加載方案
    • 3、分析應(yīng)用類(lèi)加載器的加載過(guò)程
  • 總結(jié)

    - 前言 -

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

    - JVM 類(lèi)加載器 -

    1、JVM類(lèi)加載器

    說(shuō)起 Tomcat 類(lèi)加載器,就不得不先簡(jiǎn)單說(shuō)一下 JVM 類(lèi)加載器,如下圖所示:

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

    這些類(lèi)加載器的工作原理是一樣的,區(qū)別是它們的加載路徑不同,也就是說(shuō) findClass 這個(gè)方法查找的路徑不同。

    雙親委托機(jī)制是為了保證一個(gè) Java 類(lèi)在 JVM 中是唯一的,假如你不小心寫(xiě)了一個(gè)與 JRE 核心類(lèi)同名的類(lèi),比如 Object 類(lèi),雙親委托機(jī)制能保證加載的是 JRE 里的那個(gè) Object 類(lèi),而不是你寫(xiě)的 Object 類(lèi)。

    這是因?yàn)?AppClassLoader 在加載你的 Object 類(lèi)時(shí),會(huì)委托給 ExtClassLoader 去加載,而 ExtClassLoader 又會(huì)委托給 BootstrapClassLoader,BootstrapClassLoader 發(fā)現(xiàn)自己已經(jīng)加載過(guò)了 Object 類(lèi),會(huì)直接返回,不會(huì)去加載你寫(xiě)的 Object 類(lèi)。

    這里請(qǐng)注意,類(lèi)加載器的父子關(guān)系不是通過(guò)繼承來(lái)實(shí)現(xiàn)的,比如 AppClassLoader 并不是 ExtClassLoader 的子類(lèi),而是說(shuō) AppClassLoader 的 parent 成員變量指向 ExtClassLoader 對(duì)象。同樣的道理,如果你要自定義類(lèi)加載器,不去繼承 AppClassLoader,而是繼承 ClassLoader 抽象類(lèi),再重寫(xiě) findClass 和 loadClass 方法即可,Tomcat 就是通過(guò)自定義類(lèi)加載器來(lái)實(shí)現(xiàn)自己的類(lèi)加載邏輯。不知道你發(fā)現(xiàn)沒(méi)有,如果你要打破雙親委托機(jī)制,就需要重寫(xiě) loadClass 方法,因?yàn)?loadClass 的默認(rèn)實(shí)現(xiàn)就是雙親委托機(jī)制。

    2、類(lèi)加載器的源碼

    public abstract class ClassLoader {  //  每個(gè)類(lèi)加載器都有一個(gè)父加載器  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);   // 如果沒(méi)有加載過(guò)    if (c == null) {if (parent != null) {  //  先委托給父加載器去加載,注意這是個(gè)遞歸調(diào)用 c = parent.loadClass(name, false);} else { // 如果父加載器為空,查找 Bootstrap 加載器是不是加載過(guò)了   c = findBootstrapClassOrNull(name);}          // 如果父加載器沒(méi)加載成功,調(diào)用自己的 findClass 去加載if (c == null) {    c = findClass(name);}    }     return c;}    }    //ClassLoader 中findClass方式需要被子類(lèi)覆蓋,下面這段代碼就是對(duì)應(yīng)代碼      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){        }    }

    我們自定義類(lèi)加載器就需要重寫(xiě)ClassLoader的loadClass方法。

    - Tomcat 的類(lèi)加載機(jī)制 -

    1、加載機(jī)制的特點(diǎn)

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

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

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

    2、Tomcat 的類(lèi)加載方案

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

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

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

    3、分析應(yīng)用類(lèi)加載器的加載過(guò)程

    應(yīng)用類(lèi)加載器為WebappClassLoader ,他的loadClass在他的父類(lèi)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的本地緩存中加載類(lèi),如果找到則返回    clazz = findLoadedClass0(name);    if (clazz != null) {if (log.isDebugEnabled())    log.debug("  Returning class from cache");if (resolve)    resolveClass(clazz);return clazz;    }    // 本地緩存沒(méi)有的情況下,調(diào)用ClassLoader的findLoadedClass方法查看jvm是否已經(jīng)加載過(guò)此類(lèi),如果已經(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);    //此時(shí)的javaseClassLoader是擴(kuò)展類(lèi)加載器  是把擴(kuò)展類(lèi)加載器賦值給了javaseClassLoader    ClassLoader javaseLoader = getJavaseClassLoader();    boolean tryLoadingFromJavaseLoader;    try {      .....    //如果可以用getResource得到    //如果能用擴(kuò)展類(lèi)加載器的getResource得到就證明可以被擴(kuò)展類(lèi)加載器加載到接下來(lái)安排擴(kuò)展類(lèi)加載器加載    if (tryLoadingFromJavaseLoader) {try {    //使用擴(kuò)展類(lèi)加載器進(jìn)行加載    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就是用父類(lèi)加載器進(jìn)行加載    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 {// 本地進(jìn)行加載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    //到這里還是沒(méi)有加載上再次嘗試使用父類(lèi)加載器進(jìn)行加載    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)類(lèi)加載器,但我們debug的時(shí)候會(huì)發(fā)現(xiàn)他是擴(kuò)展類(lèi)加載器,實(shí)際中我們可以推斷出他應(yīng)該是擴(kuò)展類(lèi)加載器,因?yàn)槿绻覀兗虞d的類(lèi)在擴(kuò)展類(lèi)加載器路徑下已經(jīng)存在的話(huà),那我們直接調(diào)用系統(tǒng)類(lèi)加載器是就是錯(cuò)誤的了,下圖為debug后獲取的類(lèi)加載器的驗(yàn)證。

    總結(jié)

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

    到此這篇關(guān)于Tomcat類(lèi)加載機(jī)制的文章就介紹到這了,更多相關(guān)Tomcat類(lèi)加載機(jī)制內(nèi)容請(qǐng)搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!

    標(biāo)簽: Tomcat
    主站蜘蛛池模板: 欧美日韩在线视频一区 | 九九视频在线观看视频23 | 深夜福利视频在线观看免费视频 | 久久爱91| www亚洲成人| 亚洲 欧美 日韩在线 | 国产91精品在线 | 亚洲人成毛片线播放 | 福利视频在线午夜老司机 | 日韩经典中文字幕 | 国产一区高清 | 亚洲成a v人片在线观看 | 日韩免费视频播播 | 欧美一级毛片欧美一级成人毛片 | 国产l精品国产亚洲区久久 国产tv在线 | 在线精品视频免费观看 | 日韩欧美不卡一区二区三区 | 国产毛片精品 | 成年人色网站 | 中文字幕成人在线观看 | 国产成人精品一区二区视频 | 久草在线免费资源站 | 亚洲制服欧美自拍另类 | 性夜黄a爽爽免费视频国产 性夜影院爽黄a爽免费看网站 | 成年人在线观看视频网站 | 国产精品短视频免费观看 | 手机在线免费毛片 | 成人免费看www网址入口 | 色悠久久久久综合网伊人男男 | 亚洲精品成人中文网 | 国产99视频精品免视看9 | 日韩久操| 久久精品视频免费在线观看 | 中文字幕在线永久 | 国产高清晰在线播放 | 日本特黄特色高清免费视频 | 久久久久国产精品美女毛片 | 欧美成人性色xxxxx视频大 | 亚洲国产精久久久久久久春色 | 99热碰| 日韩美女视频网站 |