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

源碼剖析Tomcat類的加載原理

 更新時間:2023年06月18日 09:21:41   作者:編程老司機A  
這篇文章主要帶大家從源碼級深入剖析一下Tomcat類的加載原理,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以跟隨小編一起學習一下

眾所周知,Java中默認的類加載器是以父子關(guān)系存在的,實現(xiàn)了雙親委派機制進行類的加載,在前文中,我們提到了,雙親委派機制的設(shè)計是為了保證類的唯一性,這意味著在同一個JVM中是不能加載相同類庫的不同版本的類。

然而與許多服務(wù)器應(yīng)用程序一樣,Tomcat 允許容器的不同部分以及在容器上運行的不同Web應(yīng)用程序可以訪問的各種不同版本的類庫,這就要求Tomcat必須打破這種雙親委派機制,通過實現(xiàn)自定義的類加載器(即實現(xiàn)了java.lang.ClassLoader)進行類的加載。下面,就讓我們來看看Tomcat類加載原理是怎樣的。

Tomcat中有兩個最重要的類加載器,第一個便是負責Web應(yīng)用程序類加載的WebappClassLoader,另一個便是JSP Servlet類加載器`JasperLoader。

Web應(yīng)用程序類加載器(WebappClassLoader)

上代碼:

public class WebappClassLoader extends WebappClassLoaderBase {
    public WebappClassLoader() {
        super();
    }
    public WebappClassLoader(ClassLoader parent) {
        super(parent);
    } 
   ...
}

我們來看看WebappClassLoader繼承的WebappClassLoaderBase中實現(xiàn)的類加載方法loadClass

public abstract class WebappClassLoaderBase extends URLClassLoader
        implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
	//...	省略不需要關(guān)注的代碼
    protected WebappClassLoaderBase() {
        super(new URL[0]);
		// 獲取當前WebappClassLoader的父加載器系統(tǒng)類加載器
        ClassLoader p = getParent();
        if (p == null) {
            p = getSystemClassLoader();
        }
        this.parent = p;
		// javaseClassLoader變量經(jīng)過以下代碼的執(zhí)行,
		// 得到的是擴展類加載器(ExtClassLoader)
        ClassLoader j = String.class.getClassLoader();
        if (j == null) {
            j = getSystemClassLoader();
            while (j.getParent() != null) {
                j = j.getParent();
            }
        }
        this.javaseClassLoader = j;
        securityManager = System.getSecurityManager();
        if (securityManager != null) {
            refreshPolicy();
        }
    }
    //...省略不需要關(guān)注的代碼
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            if (log.isDebugEnabled()) {
                log.debug("loadClass(" + name + ", " + resolve + ")");
            }
            Class<?> clazz = null;
            // Web應(yīng)用程序停止狀態(tài)時,不允許加載新的類
            checkStateForClassLoading(name);
            // 如果之前加載過該類,就可以從Web應(yīng)用程序類加載器本地類緩存中查找,
			// 如果找到說明WebappClassLoader之前已經(jīng)加載過這個類
            clazz = findLoadedClass0(name);
            if (clazz != null) {
                if (log.isDebugEnabled()) {
                    log.debug("  Returning class from cache");
                }
                if (resolve) {
                    resolveClass(clazz);
                }
                return clazz;
            }
            // Web應(yīng)用程序本地類緩存中沒有,可以從系統(tǒng)類加載器緩存中查找,
			// 如果找到說明AppClassLoader之前已經(jīng)加載過這個類
            clazz = findLoadedClass(name);
            if (clazz != null) {
                if (log.isDebugEnabled()) {
                    log.debug("  Returning class from cache");
                }
                if (resolve) {
                    resolveClass(clazz);
                }
                return clazz;
            }
			// 將類似java.lang.String這樣的類名這樣轉(zhuǎn)換成java/lang/String
			// 這樣的資源文件名
            String resourceName = binaryNameToPath(name, false);
			// 獲取引導類加載器(BootstrapClassLoader)
            ClassLoader javaseLoader = getJavaseClassLoader();
            boolean tryLoadingFromJavaseLoader;
            try {
		    // 引導類加載器根據(jù)轉(zhuǎn)換后的類名獲取資源url,如果url不為空,就說明找到要加載的類
                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) {
                ExceptionUtils.handleThrowable(t);
                tryLoadingFromJavaseLoader = true;
            }
           // 首先,從擴展類加載器(ExtClassLoader)加載,防止Java核心API庫被Web應(yīng)用程序類隨意篡改
           if (tryLoadingFromJavaseLoader) {
                try {
                    clazz = javaseLoader.loadClass(name);
                    if (clazz != null) {
                        if (resolve) {
                            resolveClass(clazz);
                        }
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
            // 當使用安全管理器時,允許訪問這個類
            if (securityManager != null) {
                int i = name.lastIndexOf('.');
                if (i >= 0) {
                    try {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } catch (SecurityException se) {
                        String error = sm.getString("webappClassLoader.restrictedPackage", name);
                        log.info(error, se);
                        throw new ClassNotFoundException(error, se);
                    }
                }
            }
            /* 
             *  如果Web應(yīng)用程序類加載器配置為,<Loader delegate="true"/> 或者滿足下列條件的類:
             *  當前類屬于以下這些jar包中:
             *  annotations-api.jar — Common Annotations 1.2 類。
             *  catalina.jar — Tomcat 的 Catalina servlet 容器部分的實現(xiàn)。
             *  catalina-ant.jar — 可選。用于使用 Manager Web 應(yīng)用程序的 Tomcat Catalina Ant 任務(wù)。
             *  catalina-ha.jar — 可選。提供基于 Tribes 構(gòu)建的會話集群功能的高可用性包。
             *  catalina-storeconfig.jar — 可選。從當前狀態(tài)生成 XML 配置文件。
             *  catalina-tribes.jar — 可選。高可用性包使用的組通信包。
             *  ecj-*.jar — 可選。Eclipse JDT Java 編譯器用于將 JSP 編譯為 Servlet。
             *  el-api.jar — 可選。EL 3.0 API。
             *  jasper.jar — 可選。Tomcat Jasper JSP 編譯器和運行時。
             *  jasper-el.jar — 可選。Tomcat EL 實現(xiàn)。
             *  jaspic-api.jar — JASPIC 1.1 API。
             *  jsp-api.jar — 可選。JSP 2.3 API。
             *  servlet-api.jar — Java Servlet 3.1 API。
             *  tomcat-api.jar — Tomcat 定義的幾個接口。
             *  tomcat-coyote.jar — Tomcat 連接器和實用程序類。
             *  tomcat-dbcp.jar — 可選?;?Apache Commons Pool 2 和 Apache Commons DBCP 2 的
             *      包重命名副本的數(shù)據(jù)庫連接池實現(xiàn)。
             *  tomcat-i18n-**.jar — 包含其他語言資源包的可選 JAR。由于默認包也包含在每個單獨的JAR
             *      中,如果不需要消息國際化,可以安全地刪除它們。
             *  tomcat-jdbc.jar — 可選。另一種數(shù)據(jù)庫連接池實現(xiàn),稱為 Tomcat JDBC 池。有關(guān)詳細信息,請參閱 文檔。
             *  tomcat-jni.jar — 提供與 Tomcat Native 庫的集成。
             *  tomcat-util.jar — Apache Tomcat 的各種組件使用的通用類。
             *  tomcat-util-scan.jar — 提供 Tomcat 使用的類掃描功能。
             *  tomcat-websocket.jar — 可選。Java WebSocket 1.1 實現(xiàn)
             *  websocket-api.jar — 可選。Java WebSocket 1.1 API
             *  
             *  此處的filter方法,實際上tomcat官方將filter類加載過濾條件,看作是一種類加載器,
	     *        將其取名為CommonClassLoader
             */
            boolean delegateLoad = delegate || filter(name, true);
            // 如果ExtClassLoader沒有獲取到,說明是非JRE核心類,那么就從系統(tǒng)類加載器(也稱AppClassLoader
			// 應(yīng)用程序類加載器)加載
            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
                }
            }
            // 從Web應(yīng)用程序的類加載器(也就是WebappClassLoader)中加載類。Web應(yīng)用程序的類加載器是
			// 一個特殊的類加載器,它負責從Web應(yīng)用程序的本地庫中加載類
            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
            }
            // 經(jīng)過上面幾個步驟還未加載到類,則采用系統(tǒng)類加載器(也稱應(yīng)用程序類加載器)進行加載
            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);
    }
	//...省略不需要關(guān)注的代碼
}

綜上所述,我們得出WebappClassLoader類加載器打破了雙親委派機制,自定義類加載類的順序:

  • 擴展類加載器(ExtClassLoader)加載
  • Web應(yīng)用程序類加載器(WebappClassLoader)
  • 系統(tǒng)類加載器類(AppClassLoader)
  • 公共類加載器類(CommonClassLoader)

如果Web應(yīng)用程序類加載器配置為,,也就是WebappClassLoaderBase類的變量delegate=true時,則類加載順序變?yōu)椋?/p>

  • 擴展類加載器(ExtClassLoader)加載
  • 系統(tǒng)類加載器類(AppClassLoader)
  • 公共類加載器類(CommonClassLoader)
  • Web應(yīng)用程序類加載器(WebappClassLoader)

JSP類加載器(JasperLoader)

上代碼:

public class JasperLoader extends URLClassLoader {
    private final PermissionCollection permissionCollection;
    private final SecurityManager securityManager;
    // JSP類加載器的父加載器是Web應(yīng)用程序類加載器(WebappClassLoader)
    public JasperLoader(URL[] urls, ClassLoader parent,
                        PermissionCollection permissionCollection) {
        super(urls, parent);
        this.permissionCollection = permissionCollection;
        this.securityManager = System.getSecurityManager();
    }
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    @Override
    public synchronized Class<?> loadClass(final String name, boolean resolve)
        throws ClassNotFoundException {
        Class<?> clazz = null;
        // 從JVM的類緩存中查找
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (resolve) {
                resolveClass(clazz);
            }
            return clazz;
        }
        // 當使用SecurityManager安全管理器時,允許訪問訪類
        if (securityManager != null) {
            int dot = name.lastIndexOf('.');
            if (dot >= 0) {
                try {
                    // Do not call the security manager since by default, we grant that package.
                    if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0,dot))){
                        securityManager.checkPackageAccess(name.substring(0,dot));
                    }
                } catch (SecurityException se) {
                    String error = "Security Violation, attempt to use " +
                        "Restricted Class: " + name;
                    se.printStackTrace();
                    throw new ClassNotFoundException(error);
                }
            }
        }
       // 如果類名不是以org.apache.jsp包名開頭的,則采用WebappClassLoader加載
        if( !name.startsWith(Constants.JSP_PACKAGE_NAME + '.') ) {
            // Class is not in org.apache.jsp, therefore, have our
            // parent load it
            clazz = getParent().loadClass(name);
            if( resolve ) {
                resolveClass(clazz);
            }
            return clazz;
        }
	// 如果是org.apache.jsp包名開頭JSP類,就調(diào)用父類URLClassLoader的findClass方法
	// 動態(tài)加載類文件,解析成Class類,返回給調(diào)用方
        return findClass(name);
    }
}

下面是URLClassLoader的findClass方法,具體實現(xiàn):

protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
 				// 解析類的字節(jié)碼文件生成Class類對象
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

從源碼中我們可以看到,JSP類加載原理是,先從JVM類緩存中(也就是Bootstrap類加載器加載的類)加載,如果不是核心類庫的類,就從Web應(yīng)用程序類加載器WebappClassLoader中加載,如果還未找到,就說明是jsp類,則通過動態(tài)解析jsp類文件獲得要加載的類。

經(jīng)過上面兩個Tomcat核心類加載器的剖析,我們也就知道了Tomcat類的加載原理了。

下面我們來總結(jié)一下:Tomcat會為每個Web應(yīng)用程序創(chuàng)建一個WebappClassLoader類加載器進行類的加載,不同的類加載器實例加載的類是會被認為是不同的類,即使它們的類名相同,這樣的話就可以實現(xiàn)在同一個JVM下,允許Tomcat容器的不同部分以及在容器上運行的不同Web應(yīng)用程序可以訪問的各種不同版本的類庫。

針對JSP類,會由專門的JSP類加載器(JasperLoader)進行加載,該加載器會針對JSP類在每次加載時都會解析類文件,Tomcat容器會啟動一個后臺線程,定時檢測JSP類文件的變化,及時更新類文件,這樣就實現(xiàn)JSP文件的熱加載功能。

以上就是源碼剖析Tomcat類的加載原理的詳細內(nèi)容,更多關(guān)于Tomcat類加載的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Tomcat請求的資源[/XXX/]不可用問題解決方法(適合小白)

    Tomcat請求的資源[/XXX/]不可用問題解決方法(適合小白)

    這篇文章主要給大家介紹了關(guān)于Tomcat請求的資源[/XXX/]不可用問題解決方法,文中介紹的方法非常適合小白,文中介紹的非常詳細,對大家的學習或者工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-01-01
  • Tomcat無法映射到activiti-app導致activiti無法啟動頁面的解決方法

    Tomcat無法映射到activiti-app導致activiti無法啟動頁面的解決方法

    這篇文章主要介紹了Tomcat無法映射到activiti-app導致activiti無法啟動頁面的解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-11-11
  • 淺析Tomcat使用線程池配置高并發(fā)連接

    淺析Tomcat使用線程池配置高并發(fā)連接

    這篇文章主要介紹了Tomcat使用線程池配置高并發(fā)連接,文中提到了tomcat內(nèi)存優(yōu)化,tomcat內(nèi)存優(yōu)化主要是對?tomcat?啟動參數(shù)優(yōu)化,我們可以在?tomcat?的啟動腳本?catalina.sh?中設(shè)置JAVA_OPTS?參數(shù),需要的朋友可以參考下
    2022-10-10
  • tomcat啟動報錯jar not loaded的問題

    tomcat啟動報錯jar not loaded的問題

    這篇文章主要介紹了tomcat啟動報錯jar not loaded的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • TongWeb替換tomcat的實現(xiàn)步驟

    TongWeb替換tomcat的實現(xiàn)步驟

    隨著技術(shù)的發(fā)展和業(yè)務(wù)需求的變化,有時候我們需要將Web項目從Tomcat遷移到TongWeb,這篇文章主要介紹了TongWeb替換tomcat的實現(xiàn)步驟,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-09-09
  • 如何在IntelliJ IDEA 2018上配置Tomcat并運行第一個JavaWeb項目

    如何在IntelliJ IDEA 2018上配置Tomcat并運行第一個JavaWeb項目

    這篇文章主要介紹了在IntelliJ IDEA 2018上配置Tomcat并運行第一個JavaWeb項目,需要的朋友可以參考下
    2020-09-09
  • tomcat 騰訊云主機和微信

    tomcat 騰訊云主機和微信

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

    Tomcat和Weblogic部署純html文件過程解析

    這篇文章主要介紹了Tomcat和Weblogic部署純html文件過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • 查看tomcat并發(fā)連接數(shù)實現(xiàn)方式

    查看tomcat并發(fā)連接數(shù)實現(xiàn)方式

    文章介紹了兩種查看Tomcat并發(fā)連接數(shù)的方法:一是通過Tomcat管理控制臺,需配置賬號并登錄后查看;二是使用命令行實時查詢,并說明了相關(guān)端口設(shè)置和操作權(quán)限要求
    2025-10-10
  • 淺談Tomcat Session管理分析

    淺談Tomcat Session管理分析

    這篇文章主要介紹了淺談Tomcat Session管理分析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06

最新評論

广安市| 邵武市| 拉孜县| 阿荣旗| 罗江县| 塘沽区| 砀山县| 永修县| 靖州| 图木舒克市| 礼泉县| 玉环县| 延寿县| 巨鹿县| 清涧县| 吴忠市| 双峰县| 泰宁县| 洛浦县| 广水市| 永吉县| 中西区| 缙云县| 合水县| 南通市| 德阳市| 柏乡县| 武鸣县| 武安市| 新丰县| 昌宁县| 桦南县| 革吉县| 建平县| 耒阳市| 凤翔县| 茌平县| 荔波县| 股票| 衢州市| 通许县|