国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Tomcat源碼解讀:ClassLoader的設計

2019-11-14 22:22:34
字體:
來源:轉載
供稿:網友
Tomcat源碼解讀:ClassLoader的設計

Tomcat是一個經典的web server,學習tomcat的源碼對于我們是有很大的幫助的。前一段時間了解了tomcat的工作的大致流程,對我的新工作有了很大的幫助。剛學習了ClassLoader(學習classloader的初衷源于公司產品的一個bug),也將我對classloaderp寫成了一篇博客。為了對ClassLoader有更多的理解,現在就來看看Tomcat 6 的ClassLoader設計。

之前通過對tomcat的啟動過程、tomcat處理request的過程進行簡單的了解,了解tomcat的各個組件及其功能。在這兩個過程中,都有ClassLoader的影子,所以今天依舊從這兩個方面入手,來了解Tomcat 如何使用ClassLoader。

StandardClassLoader

Bootstrap.main()方法簡單點說就是執行4個方法:init , setAwait, load, start。

init

public void init()        throws Exception    {        // Set Catalina path        setCatalinaHome();        setCatalinaBase();        initClassLoaders();        Thread.currentThread().setContextClassLoader(catalinaLoader);        SecurityClassLoad.securityClassLoad(catalinaLoader);        // Load our startup class and call its PRocess() method        if (log.isDebugEnabled())            log.debug("Loading startup class");        Class startupClass =            catalinaLoader.loadClass            ("org.apache.catalina.startup.Catalina");        Object startupInstance = startupClass.newInstance();        // Set the shared extensions class loader        if (log.isDebugEnabled())            log.debug("Setting startup class properties");        String methodName = "setParentClassLoader";        Class paramTypes[] = new Class[1];        paramTypes[0] = Class.forName("java.lang.ClassLoader");        Object paramValues[] = new Object[1];        paramValues[0] = sharedLoader;        Method method =            startupInstance.getClass().getMethod(methodName, paramTypes);        method.invoke(startupInstance, paramValues);        catalinaDaemon = startupInstance;    }

在init方法中調用首先initClassLoader方法來初始化Tomcat的ClassLoader模塊,然后是使用剛自定義類加載器加載catalinaLoader 來加載org.apache.catalina.startup.Catalina 類。接下來是調用Catalina中的setParentClassLoader方法。

那就看看initClassLoader中將Tomcat的ClassLoader模塊初始化成什么樣子的:

private void initClassLoaders() {        try {            commonLoader = createClassLoader("common", null);            if( commonLoader == null ) {                // no config file, default to this loader - we might be in a 'single' env.                commonLoader=this.getClass().getClassLoader();            }            catalinaLoader = createClassLoader("server", commonLoader);            sharedLoader = createClassLoader("shared", commonLoader);        } catch (Throwable t) {            log.error("Class loader creation threw exception", t);            System.exit(1);        }}

這里其實就是創建了3個ClassLoader,分別是commonLoader, catalinaLoader, sharedLoader。而其實它們之間是有這某種關系的:

private ClassLoader createClassLoader(String name, ClassLoader parent)        throws Exception {String value = CatalinaProperties.getProperty(name + ".loader");        if ((value == null) || (value.equals("")))            return parent;// some statementClassLoader classLoader = ClassLoaderFactory.createClassLoader            (locations, types, parent);   // some statement}

也就是說啟動過程初始化后的ClassLoader模型為:

其中頂部的3個classLoader對象都由JDK提供的。commonLoader, catalinaLoader, sharedLoader它們三個有一個共同的名字:StandardClassLoader。

bootstrapClassLoader是加載java_home/jre/lib目錄下的個別jar包(不是全部)

extClassLoader是加載java_home/jre/lib/ext目錄下的jar包

AppClassLoader是加載classpath中指定的jar包

在catalina.properties文件中則指出了這3個ClassLoader默認的加載路徑。

common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jarserver.loader=shared.loader=

這個文件是在jar包中的,在使用tomcat時是不可以修改的。如果想要修改默認的加載路徑,該怎么辦呢?可以在catalina.config配置

想要了解是什么原因,可以參考CatalinaProperties類的實現。

上面說了Bootstrap的main方法中其實就是調用了4個方法init,setAwait, start, load,除了init外,另外3個方法其實就是調用Catalina對象的對應的setAwait,start, load方法。

從init方法的實現中,可以知道,在Bootstrap類中,除了Catalina類是由catalinaClassLoader加載的之外,其余的類都是JDK提供的ClassLoader加載的。也就是說根據上節學習的內容,Catalina類在當前類Bootstrap類中是不能直接調用的。然而這里又要調用Catalina中的方法,我在上節的測試中,采取的是另外啟動一個線程來解決的。今天就來看看Tomcat中如何解決這樣的應用場景的:

1)init中調用Catalina的setParentClassLoader方法

Object startupInstance = startupClass.newInstance();        // Set the shared extensions class loader        if (log.isDebugEnabled())            log.debug("Setting startup class properties");        String methodName = "setParentClassLoader";        Class paramTypes[] = new Class[1];        paramTypes[0] = Class.forName("java.lang.ClassLoader");        Object paramValues[] = new Object[1];        paramValues[0] = sharedLoader;        Method method =            startupInstance.getClass().getMethod(methodName, paramTypes);        method.invoke(startupInstance, paramValues);

2) setAwait中調用catalina的setAwait方法

public void setAwait(boolean await)        throws Exception {        Class paramTypes[] = new Class[1];        paramTypes[0] = Boolean.TYPE;        Object paramValues[] = new Object[1];        paramValues[0] = new Boolean(await);        Method method =             catalinaDaemon.getClass().getMethod("setAwait", paramTypes);        method.invoke(catalinaDaemon, paramValues);    }

3)start調用catalina的start方法

public void start()        throws Exception {        if( catalinaDaemon==null ) init();        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);        method.invoke(catalinaDaemon, (Object [])null);    }

4)load調用catalina的load方法

private void load(String[] arguments)        throws Exception {        // Call the load() method        String methodName = "load";        Object param[];        Class paramTypes[];        if (arguments==null || arguments.length==0) {            paramTypes = null;            param = null;        } else {            paramTypes = new Class[1];            paramTypes[0] = arguments.getClass();            param = new Object[1];            param[0] = arguments;        }        Method method =             catalinaDaemon.getClass().getMethod(methodName, paramTypes);        if (log.isDebugEnabled())            log.debug("Calling startup class " + method);        method.invoke(catalinaDaemon, param);    }

它們都是使用反射來處理。

現在看來,要解決由不同classLoader加載器加載的類之前的方法調用,就有2種處理方案了:

方案一:另啟動一個線程

方案二:使用反射。

Catalina是由catalinaClassLoader加載的,Tomcat啟動過程會將Tomcat的相關組件加載并初始化,而這些過程的入口都在Catalina中。所以呢Tomcat中絕在部分組件應當都是由catalinaClassLoader加載的。這句話說的可能有些滿,不過呢,我這么說也是有原因的:

在init方法中有這么一個過程:SecurityClassLoad.securityClassLoad(catalinaLoader);

下面來看看這個過程的真面目:

public static void securityClassLoad(ClassLoader loader)        throws Exception {        if( System.getSecurityManager() == null ){            return;        }                loadCorePackage(loader);        loadLoaderPackage(loader);        loadServletsPackage(loader);        loadsessionPackage(loader);        loadUtilPackage(loader);        loadJavaxPackage(loader);        loadCoyotePackage(loader);        loadHttp11Package(loader);        loadTomcatPackage(loader);}private final static void loadCorePackage(ClassLoader loader)        throws Exception {        String basePackage = "org.apache.catalina.";        loader.loadClass            (basePackage +             "core.applicationContextFacade$1");        loader.loadClass            (basePackage +             "core.ApplicationDispatcher$PrivilegedForward");        loader.loadClass            (basePackage +             "core.ApplicationDispatcher$PrivilegedInclude");        loader.loadClass            (basePackage +             "core.ContainerBase$PrivilegedAddChild");        loader.loadClass            (basePackage +             "core.StandardWrapper$1");        loader.loadClass            (basePackage +              "core.ApplicationHttpRequest$AttributeNamesEnumerator");    }

securityClassLoad(ClassLoader loader)采用了門面(facade)模式來加載各個類。這里只列出了loadCorePackage的實現。其它方法的實現與這個是完全一樣的。所以上面 我才說Tomcat中絕在部分組件應當都是由catalinaClassLoader加載的。

同時根據這個方法,也可以看出,要加載內部類,要用外部類與內部類類名之間加上$

===============================================================================

WebappClassLoader

上面說的其實就是Tomcat中的StandardClassLoader,Tomcat中還有一種WebappClassLoader。根據名字就可以看出來,這個ClassLoader是用于加載各個Web Application而設計的。Tomcat的各個組件中與Web Application有對應關系的,也就是StandardContext。為何這么說呢?我們在Web應用中使用的ServletConext(Java EE的標準API)在Tomcat中由ApplicationContext來實現,而ApplicationContext其實就是StandardContext的委托。

這個Classloader是用于加載web應用程序中的類。這個類以后會有專門了解一下。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阳江市| 三原县| 凌海市| 土默特左旗| 沂源县| 武强县| 梧州市| 绥宁县| 正宁县| 崇仁县| 手游| 且末县| 桑植县| 广宗县| 荔波县| 登封市| 灌阳县| 宾川县| 开原市| 泰来县| 通州市| 霸州市| 安阳市| 达日县| 乌苏市| 渑池县| 白城市| 偏关县| 吉林省| 南部县| 山阴县| 宜黄县| 仙桃市| 故城县| 洮南市| 泰来县| 塘沽区| 平阳县| 禹州市| 长治县| 修武县|