黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

java類(lèi)加載器-Tomcat類(lèi)加載器

系統(tǒng) 2349 0

  在上文中,已經(jīng)介紹了 系統(tǒng)類(lèi)加載器 以及類(lèi)加載器的相關(guān)機(jī)制,還自定制類(lèi)加載器的方式。接下來(lái)就以tomcat6為例看看tomat是如何使用自定制類(lèi)加載器的。(本介紹是基于tomcat6.0.41,不同版本可能存在差異!)

網(wǎng)上所描述的tomcat類(lèi)加載器

  在網(wǎng)上搜一下“tomcat類(lèi)加載器”會(huì)發(fā)現(xiàn)有大量的文章,在此我偷個(gè)懶,^_^把網(wǎng)上對(duì)tomcat類(lèi)加載器的描述重說(shuō)一下吧。

java類(lèi)加載器-Tomcat類(lèi)加載器

  • CommonClassLoader:加載的類(lèi)目錄通過(guò){tomcat}/conf/catalina.properties中的common.loader指定,以SystemClassLoader為parent(目前默認(rèn)定義是common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar)
  • CatalinaClassLoader?? :加載的類(lèi)目錄通過(guò){tomcat}/conf/catalina.properties中server.loader指定,以CommonClassLoader為parent,如果server.loader配置為空,則ServerClassLoader 與CommonClassLoader是同一個(gè)(默認(rèn)server.loader配置為空)
  • SharedClassLoader:加載的類(lèi)目錄通過(guò){tomcat}/conf/catalina.properties中share.loader指定,以CommonClassLoader為parent,如果server.loader配置為空,則CatalinaClassLoader 與CommonClassLoader是同一個(gè)(默認(rèn)share.loader配置為空)
  • WebappClassLoader:每個(gè)Context一個(gè)WebappClassLoader實(shí)例,負(fù)責(zé)加載context的/WEB-INF/lib和/WEB-INF/classes目錄,context間的隔離就是通過(guò)不同的WebappClassLoader來(lái)做到的。由于類(lèi)定義一旦加載就不可改變,因此要實(shí)現(xiàn)tomcat的context的reload功能,實(shí)際上是通過(guò)新建一個(gè)新的WebappClassLoader來(lái)做的,因此reload的做法實(shí)際上代價(jià)是很高昂的,需要注意的是,JVM內(nèi)存的Perm區(qū)是只吃不拉的,因此拋棄掉的WebappClassLoader加載的類(lèi)并不會(huì)被JVM釋放,因此tomcat的reload功能如果應(yīng)用定義的類(lèi)比較多的話(huà),reload幾次就OutOfPermSpace異常了。
  • JasperLoader:每個(gè)JSP一個(gè)JasperLoader實(shí)例,與WebappClassLoader做法類(lèi)似,JSP支持修改生效是通過(guò)丟棄舊的JasperLoader,建一個(gè)新的JasperLoader來(lái)做到的,同樣的,存在輕微的PermSpace的內(nèi)存泄露的情況

以上對(duì)個(gè)個(gè)classloader的作用做了介紹,但請(qǐng)讀者不要搞混淆了,上邊說(shuō)的個(gè)個(gè)類(lèi)加載器只是類(lèi)加載器的名字,不是類(lèi)加載類(lèi)的名字。上邊的圖是看到網(wǎng)上資料的說(shuō)明繪制的,但是與實(shí)際源碼中的結(jié)構(gòu)還是差異挺大的。(沒(méi)有研究是不是因?yàn)閠omcat的版本所致)。下面就詳細(xì)介紹下tomcat源碼中類(lèi)加載器的組織結(jié)構(gòu)。

tomcat源碼中類(lèi)加載器的結(jié)構(gòu)分析

首先要說(shuō)明是tomcat默認(rèn)配置下的情況。那接下來(lái)看看tomcat啟動(dòng)時(shí)的類(lèi)初始化情況,這是BootStrap類(lèi)的類(lèi)初始化方法:

??

        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);

        }

    }


    

?

可以看到,在創(chuàng)建commonLoader時(shí)傳的父類(lèi)加載器是null。跟蹤下去會(huì)發(fā)現(xiàn)commonLoader的父類(lèi)加載器確實(shí)是null。有朋友可能想,tomcat在啟動(dòng)時(shí)肯定也要依賴(lài)jdk核心庫(kù),parent是null那怎么委托給parent去加載核心庫(kù)的類(lèi)了啊。這里大家不要忘了ClassLoader類(lèi)的loadClass方法:

      try {

                    if (parent != null) {

                        c = parent.loadClass(name, false);

                    } else {

                        c = findBootstrapClassOrNull(name);//沒(méi)有父類(lèi)加載器時(shí)使用bootstrap類(lèi)加載器

                    }

                } catch (ClassNotFoundException e) {

                    // ClassNotFoundException thrown if class not found

                    // from the non-null parent class loader

                }


    

通過(guò)以上initClassLoaders方法我們也能看到catalinaLoader和sharedLoader的父類(lèi)加載器都是commonLoader,跟上邊圖的類(lèi)加載器結(jié)構(gòu)符合。但是commonLoader、catalinaLoader和sharedLoader的創(chuàng)建都是依賴(lài)tomcat安裝目錄下conf/catalina.properties的配置。默認(rèn)情況配置是:

  • common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
  • server.loader=
  • shared.loader=

由于server.loader和shared.loader的配置為空,所以其實(shí)commonLoader、catalinaLoader和sharedLoader都是指向同一個(gè)類(lèi)加載器實(shí)例,看代碼如下:(限于篇幅只貼部分代碼)

       private ClassLoader createClassLoader(String name, ClassLoader parent)

        throws Exception {



        String value = CatalinaProperties.getProperty(name + ".loader");

        if ((value == null) || (value.equals("")))

            return parent;


    

而他們指向那個(gè)類(lèi)加載器類(lèi)的實(shí)例呢?跟蹤到最后我們發(fā)現(xiàn)如下代碼:

       StandardClassLoader classLoader = null;

        if (parent == null)

            classLoader = new StandardClassLoader(array);

        else

            classLoader = new StandardClassLoader(array, parent);

        return (classLoader);


    

就是StandardClassLoader的實(shí)例,下文再對(duì)StandardClassLoader進(jìn)行源碼講解。

  接下來(lái)再看看webappclassloader的創(chuàng)建過(guò)程,webappclassLoader是在WebappLoader類(lèi)中的createClassLoader方法中通過(guò)反射實(shí)例化的。下邊是源代碼:

          private WebappClassLoader createClassLoader()

        throws Exception {



        Class clazz = Class.forName(loaderClass);//loaderClass="org.apache.catalina.loader.WebappClassLoader"

        WebappClassLoader classLoader = null;



        if (parentClassLoader == null) {

            parentClassLoader = container.getParentClassLoader();

        }

        Class[] argTypes = { ClassLoader.class };

        Object[] args = { parentClassLoader };

        Constructor constr = clazz.getConstructor(argTypes);

        classLoader = (WebappClassLoader) constr.newInstance(args);



        return classLoader;



    }


    

可以看到他的parent是通過(guò)調(diào)用container.getParentlassLoader()獲得的(如果對(duì)tomcat的結(jié)構(gòu)不熟悉,請(qǐng)看這篇 文章 )跟蹤到最后我們發(fā)現(xiàn)它調(diào)用了ContainerBase的這個(gè)方法:

          public ClassLoader getParentClassLoader() {

        if (parentClassLoader != null)

            return (parentClassLoader);

        if (parent != null) {

            return (parent.getParentClassLoader());

        }

        return (ClassLoader.getSystemClassLoader());



    }


    

通過(guò)默認(rèn)配置下debug可以知道最后是返回的systemclassloader,也就是說(shuō)WebappClassLoader的父類(lèi)加載器是systemclassloader也就是 上篇文章 說(shuō)的App ClassLoader。

(由于JasperLoader本人還沒(méi)有做分析,先不進(jìn)行講解了)

tomcat類(lèi)加載器的實(shí)現(xiàn)方式分析

  上文說(shuō)到了commonLoader、catalinaLoader和sharedLoader都是指向StandardClassLoader的實(shí)例,來(lái)先看一看StandardClassLoader的源碼實(shí)現(xiàn):

      public class StandardClassLoader

    extends URLClassLoader

    implements StandardClassLoaderMBean {



	public StandardClassLoader(URL repositories[]) {

        super(repositories);

    }



    public StandardClassLoader(URL repositories[], ClassLoader parent) {

        super(repositories, parent);

    }



}


    

有沒(méi)有感到你的意外啊,對(duì)的就是這么簡(jiǎn)單,這跟我 上篇文章 說(shuō)的最簡(jiǎn)單的實(shí)現(xiàn)方式一樣。(上篇文章做了解讀,這里不再做說(shuō)明了)

  我們?cè)賮?lái)看看webappclassLoader,他的實(shí)現(xiàn)類(lèi)就是org.apache.catalina.loader.WebappClassLoader,此類(lèi)加載器也是繼承自URLClassLoader,但是它覆蓋了loadClass方法和findClass方法。這個(gè)類(lèi)有三千多行這里就不將代碼全部貼出來(lái)了。

      public Class loadClass(String name, boolean resolve)

        throws ClassNotFoundException {



        if (log.isDebugEnabled())

            log.debug("loadClass(" + name + ", " + resolve + ")");

        Class clazz = null;



        // Log access to stopped classloader

        if (!started) {

            try {

                throw new IllegalStateException();

            } catch (IllegalStateException e) {

                log.info(sm.getString("webappClassLoader.stopped", name), e);

            }

        }



        // (0) 檢查WebappClassLoader之前是否已經(jīng)load過(guò)這個(gè)資源

clazz = findLoadedClass0(name);

        if (clazz != null) {

            if (log.isDebugEnabled())

                log.debug("  Returning class from cache");

            if (resolve)

                resolveClass(clazz);

            return (clazz);

        }



        // (0.1) 檢查ClassLoader之前是否已經(jīng)load過(guò)

        clazz = findLoadedClass(name);

        if (clazz != null) {

            if (log.isDebugEnabled())

                log.debug("  Returning class from cache");

            if (resolve)

                resolveClass(clazz);

            return (clazz);

        }



        // (0.2) 先檢查系統(tǒng)ClassLoader,因此WEB-INF/lib和WEB-INF/classes或{tomcat}/libs下的類(lèi)定義不能覆蓋JVM 底層能夠查找到的定義(譬如不能通過(guò)定義java.lang.Integer替代底層的實(shí)現(xiàn)

        try {

            clazz = system.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);

                }

            }

        }



        //這是一個(gè)很奇怪的定義,JVM的類(lèi)加載機(jī)制建議先由parent去load,load不到自己再去load(見(jiàn)上篇文章),而Servelet規(guī)范的建議則恰好相反,Tomcat的實(shí)現(xiàn)則做個(gè)折中,由用戶(hù)去決定(context的 delegate定義),默認(rèn)使用Servlet規(guī)范的建議,即delegate=false

        boolean delegateLoad = delegate || filter(name);



        // (1) 先由parent去嘗試加載,如上說(shuō)明,除非設(shè)置了delegate,否則這里不執(zhí)行

        if (delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader1 " + parent);

            ClassLoader loader = parent;

            

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }



        // (2) 到WEB-INF/lib和WEB-INF/classes目錄去搜索,細(xì)節(jié)部分可以再看一下findClass,會(huì)發(fā)現(xiàn)默認(rèn)是先搜索WEB-INF/classes后搜索WEB-INF/lib

        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) {

            ;

        }



        // (3) 由parent再去嘗試加載一下

        if (!delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader at end: " + parent);

            ClassLoader loader = parent;

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }



        throw new ClassNotFoundException(name);

    }


    

?

java類(lèi)加載器-Tomcat類(lèi)加載器


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論