日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Tomcat源码解析六:Tomcat类加载器机制

發(fā)布時(shí)間:2024/1/17 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Tomcat源码解析六:Tomcat类加载器机制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

要說(shuō)Tomcat的Classloader機(jī)制,我們還得從Bootstrap開(kāi)始。在BootStrap初始化的時(shí)候,調(diào)用了org.apache.catalina.startup.Bootstrap#initClassLoaders方法,這個(gè)方法里面創(chuàng)建了3個(gè)ClassLoader,它們分別是commonLoader,catalinaLoader,sharedLoader,其中catalinaLoader,sharedLoader的父親加載器是commonLoader,initClassLoaders執(zhí)行的過(guò)程中會(huì)執(zhí)行createClassLoader,而createClassLoader是根據(jù)conf/catalina.properties文件中common.loader,server.loader,shared.loader的值來(lái)初始化,它的代碼如下:

[java]?view plaincopy
  • org.apache.catalina.startup.Bootstrap#createClassLoader??
  • [java]?view plaincopy
  • rivate?ClassLoader?createClassLoader(String?name,?ClassLoader?parent)??
  • ????throws?Exception?{??
  • ??
  • ????String?value?=?CatalinaProperties.getProperty(name?+?".loader");??
  • ????//?1???
  • ????if?((value?==?null)?||?(value.equals("")))??
  • ????????return?parent;??
  • ??
  • ????//?2??
  • ????value?=?replace(value);??
  • ??
  • ????List<Repository>?repositories?=?new?ArrayList<Repository>();??
  • ??
  • ????StringTokenizer?tokenizer?=?new?StringTokenizer(value,?",");??
  • ????while?(tokenizer.hasMoreElements())?{??
  • ????????String?repository?=?tokenizer.nextToken().trim();??
  • ????????if?(repository.length()?==?0)?{??
  • ????????????continue;??
  • ????????}??
  • ??
  • ????????//?Check?for?a?JAR?URL?repository??
  • ????????try?{??
  • ????????????@SuppressWarnings("unused")??
  • ????????????URL?url?=?new?URL(repository);??
  • ????????????repositories.add(??
  • ????????????????????new?Repository(repository,?RepositoryType.URL));??
  • ????????????continue;??
  • ????????}?catch?(MalformedURLException?e)?{??
  • ????????????//?Ignore??
  • ????????}??
  • ??
  • ????????//?Local?repository??
  • ????????if?(repository.endsWith("*.jar"))?{??
  • ????????????repository?=?repository.substring??
  • ????????????????(0,?repository.length()?-?"*.jar".length());??
  • ????????????repositories.add(??
  • ????????????????????new?Repository(repository,?RepositoryType.GLOB));??
  • ????????}?else?if?(repository.endsWith(".jar"))?{??
  • ????????????repositories.add(??
  • ????????????????????new?Repository(repository,?RepositoryType.JAR));??
  • ????????}?else?{??
  • ????????????repositories.add(??
  • ????????????????????new?Repository(repository,?RepositoryType.DIR));??
  • ????????}??
  • ????}??
  • ????//?3??
  • ????ClassLoader?classLoader?=?ClassLoaderFactory.createClassLoader??
  • ????????(repositories,?parent);??
  • ??
  • ??
  • ????return?classLoader;??
  • ??
  • }??

  • 以上代碼刪除了與本篇無(wú)關(guān)的代碼,下面我們分別來(lái)分析一下標(biāo)注的地方:

  • 標(biāo)注1的代碼(第5行)判斷如果catalina.properties中沒(méi)有配置對(duì)應(yīng)的loader屬性的話,直接返回父加載器,而默認(rèn)情況下,server.loader,shared.loader為空,那么此時(shí)的catalinaLoader,sharedLoader其實(shí)是同一個(gè)ClassLoader.
  • 標(biāo)注2(第9行)的地方根據(jù)環(huán)境變量的配置替換字符串中的值.默認(rèn)情況下,common.loader的值為common.loader=${catalina.base}/lib,${catalina.base}/lib/.jar,${catalina.home}/lib,${catalina.home}/lib/.jar,這里會(huì)將catalina.base和catalina.home用環(huán)境變量的值替換。
  • 標(biāo)注3(第46行)的代碼最終調(diào)用org.apache.catalina.startup.ClassLoaderFactory#createClassLoader靜態(tài)工廠方法創(chuàng)建了URLClassloader的實(shí)例,而具體的URL其實(shí)就是*.loader屬性配置的內(nèi)容,此外如果parent為null的話,則直接用系統(tǒng)類加載器。
  • 上面分析了Tomcat在啟動(dòng)的時(shí)候,初始化的幾個(gè)ClassLoader,接下來(lái)我們?cè)賮?lái)繼續(xù)看看,這些ClassLoader具體都用在什么地方。

    我們接著來(lái)看org.apache.catalina.startup.Bootstrap#init方法,在初始化完3個(gè)classLoader以后,接下來(lái)首先通過(guò)catalinaLoader加載了org.apache.catalina.startup.Catalinal類,然后通過(guò)放射調(diào)用了org.apache.catalina.startup.Catalina#setParentClassLoader,具體代碼片段如下:

    [java]?view plaincopy
  • org.apache.catalina.startup.Bootstrap#init??
  • [java]?view plaincopy
  • Class<?>?startupClass?=??
  • ????catalinaLoader.loadClass??
  • ????("org.apache.catalina.startup.Catalina");??
  • Object?startupInstance?=?startupClass.newInstance();??
  • ??
  • 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);??
  • 通過(guò)上面的代碼,我們可以清楚的看到調(diào)用了Catalina的setParentClassLoader放,那么到這里我們可能又要想知道,設(shè)置了parentClassLoader以后,sharedLoader又是在哪里使用的呢?這就需要我們接著來(lái)分析容器啟動(dòng)的代碼。我們通過(guò)查看 org.apache.catalina.startup.Catalina#getParentClassLoader 調(diào)用棧,我們看到在StandardContext的startInternal方法中調(diào)用了它,那么我們查看一下它的代碼,包含了如下代碼片段:

    [java]?view plaincopy
  • org.apache.catalina.core.StandardContext#startInternal??
  • [java]?view plaincopy
  • if?(getLoader()?==?null)?{??
  • ????????????WebappLoader?webappLoader?=?new?WebappLoader(getParentClassLoader());??
  • ????????????webappLoader.setDelegate(getDelegate());??
  • ????????????setLoader(webappLoader);??
  • }??
  • try?{??
  • ??
  • ????if?(ok)?{??
  • ??
  • ????????//?Start?our?subordinate?components,?if?any??
  • ????????if?((loader?!=?null)?&&?(loader?instanceof?Lifecycle))??
  • ????????????((Lifecycle)?loader).start();??
  • ????????//other?code??????
  • ????}??
  • catch(Exception?e){??
  • }??
  • 通過(guò)查看上面的代碼,我們看到在StandardContext啟動(dòng)的時(shí)候,會(huì)創(chuàng)建webapploader,創(chuàng)建webapploader的時(shí)候會(huì)將getParentClassLoader方法返回的結(jié)果(這里返回的其實(shí)就是sharedLoader)賦值給自己的parentClassLoader變量,接著又會(huì)調(diào)用到Webapploader的start方法,因?yàn)閃ebappLoader符合Tomcat組件生命周期管理的模板方法模式,因此會(huì)調(diào)用到它的startInternal方法。我們接下來(lái)就來(lái)看看WebappLoader的startInternal,我們摘取一部分與本篇相關(guān)的代碼片段如下:

    [java]?view plaincopy
  • org.apache.catalina.loader.WebappLoader#startInternal??
  • [java]?view plaincopy
  • classLoader?=?createClassLoader();??
  • classLoader.setResources(container.getResources());??
  • classLoader.setDelegate(this.delegate);??
  • classLoader.setSearchExternalFirst(searchExternalFirst);??
  • 從上的代碼可以看到調(diào)用了createClassLoader方法創(chuàng)建一個(gè)classLoader,那么我們?cè)倏磥?lái)看看createClassLoader的代碼:
    [java]?view plaincopy
  • org.apache.catalina.loader.WebappLoader#createClassLoader??
  • [java]?view plaincopy
  • private?WebappClassLoader?createClassLoader()??
  • ????throws?Exception?{??
  • ??
  • ????Class<?>?clazz?=?Class.forName(loaderClass);??
  • ????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;??
  • ??
  • }??
  • 在上面的代碼里面,loaderClass是WebappLoader的實(shí)例變量,其值為org.apache.catalina.loader.WebappClassLoader,那么上面的代碼其實(shí)就是通過(guò)反射調(diào)用了WebappClassLoader的構(gòu)造函數(shù),然后傳遞了sharedLoader作為其父親加載器。

    代碼閱讀到這里,我們已經(jīng)基本清楚了Tomcat中ClassLoader的總體結(jié)構(gòu),總結(jié)如下: 在Tomcat存在common,cataina,shared三個(gè)公共的classloader,默認(rèn)情況下,這三個(gè)classloader其實(shí)是同一個(gè),都是common classloader,而針對(duì)每個(gè)webapp,也就是context(對(duì)應(yīng)代碼中的StandardContext類),都有自己的WebappClassLoader來(lái)加載每個(gè)應(yīng)用自己的類。上面的描述,我們可以通過(guò)下圖形象化的描述:





    清楚了Tomcat總體的ClassLoader結(jié)構(gòu)以后,咋們就來(lái)進(jìn)一步來(lái)分析一下WebAppClassLoader的代碼,我們知道Java的ClassLoader機(jī)制有parent-first的機(jī)制,而這種機(jī)制是在loadClass方法保證的,一般情況下,我們只需要重寫findClass方法就好了,而對(duì)于WebAppClassLoader,通過(guò)查看源代碼,我們發(fā)現(xiàn)loadClass和findClass方法都進(jìn)行了重寫,那么我們首先就來(lái)看看它的loadClass方法,它的代碼如下:

    org.apache.catalina.loader.WebappClassLoader#loadClass

    [java]?view plaincopy
  • public?synchronized?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)?Check?our?previously?loaded?local?class?cache??
  • ????//?1???
  • ????clazz?=?findLoadedClass0(name);??
  • ????if?(clazz?!=?null)?{??
  • ????????if?(log.isDebugEnabled())??
  • ????????????log.debug("??Returning?class?from?cache");??
  • ????????if?(resolve)??
  • ????????????resolveClass(clazz);??
  • ????????return?(clazz);??
  • ????}??
  • ??
  • ????//?(0.1)?Check?our?previously?loaded?class?cache??
  • ????//?2??
  • ????clazz?=?findLoadedClass(name);??
  • ????if?(clazz?!=?null)?{??
  • ????????if?(log.isDebugEnabled())??
  • ????????????log.debug("??Returning?class?from?cache");??
  • ????????if?(resolve)??
  • ????????????resolveClass(clazz);??
  • ????????return?(clazz);??
  • ????}??
  • ??
  • ????//?(0.2)?Try?loading?the?class?with?the?system?class?loader,?to?prevent??
  • ????//???????the?webapp?from?overriding?J2SE?classes??
  • ????//?3???
  • ????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);??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??
  • ????//4???
  • ????boolean?delegateLoad?=?delegate?||?filter(name);??
  • ??
  • ????//?(1)?Delegate?to?our?parent?if?requested??
  • ????//?5???
  • ????if?(delegateLoad)?{??
  • ????????if?(log.isDebugEnabled())??
  • ????????????log.debug("??Delegating?to?parent?classloader1?"?+?parent);??
  • ????????ClassLoader?loader?=?parent;??
  • ????????if?(loader?==?null)??
  • ????????????loader?=?system;??
  • ????????try?{??
  • ????????????clazz?=?Class.forName(name,?false,?loader);??
  • ????????????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");??
  • ????//?6???
  • ????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??
  • ????}??
  • ??
  • ????//?(3)?Delegate?to?parent?unconditionally??
  • ????//?7??
  • ????if?(!delegateLoad)?{??
  • ????????if?(log.isDebugEnabled())??
  • ????????????log.debug("??Delegating?to?parent?classloader?at?end:?"?+?parent);??
  • ????????ClassLoader?loader?=?parent;??
  • ????????if?(loader?==?null)??
  • ????????????loader?=?system;??
  • ????????try?{??
  • ????????????clazz?=?Class.forName(name,?false,?loader);??
  • ????????????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);??
  • ??
  • }??
  • 我們一步步的來(lái)分析一下上面的代碼做了什么事情。

  • 標(biāo)注1(第18行)代碼,首先從當(dāng)前ClassLoader的本地緩存中加載類,如果找到則返回。
  • 標(biāo)注2(第29行)代碼,在本地緩存沒(méi)有的情況下,調(diào)用ClassLoader的findLoadedClass方法查看jvm是否已經(jīng)加載過(guò)此類,如果已經(jīng)加載則直接返回。
  • 標(biāo)注3(第41行)代碼,通過(guò)系統(tǒng)的來(lái)加載器加載此類,這里防止應(yīng)用寫的類覆蓋了J2SE的類,這句代碼非常關(guān)鍵,如果不寫的話,就會(huì)造成你自己寫的類有可能會(huì)把J2SE的類給替換調(diào),另外假如你寫了一個(gè)javax.servlet.Servlet類,放在當(dāng)前應(yīng)用的WEB-INF/class中,如果沒(méi)有此句代碼的保證,那么你自己寫的類就會(huì)替換到Tomcat容器Lib中包含的類。
  • 標(biāo)注4(第68行)代碼,判斷是否需要委托給父類加載器進(jìn)行加載,delegate屬性默認(rèn)為false,那么delegatedLoad的值就取決于filter的返回值了,filter方法中根據(jù)包名來(lái)判斷是否需要進(jìn)行委托加載,默認(rèn)情況下會(huì)返回false.因此delegatedLoad為false
  • 標(biāo)注5(第72行)代碼,因?yàn)閐elegatedLoad為false,那么此時(shí)不會(huì)委托父加載器去加載,這里其實(shí)是沒(méi)有遵循parent-first的加載機(jī)制。
  • 標(biāo)注6(第96行)調(diào)用findClass方法在webapp級(jí)別進(jìn)行加載
  • 標(biāo)注7(第111行)如果還是沒(méi)有加載到類,并且不采用委托機(jī)制的話,則通過(guò)父類加載器去加載。
  • 通過(guò)上面的描述,我們可以知道Tomcat在加載webapp級(jí)別的類的時(shí)候,默認(rèn)是不遵守parent-first的,這樣做的好處是更好的實(shí)現(xiàn)了應(yīng)用的隔離,但是壞處就是加大了內(nèi)存浪費(fèi),同樣的類庫(kù)要在不同的app中都要加載一份。

    上面分析完了loadClass,我們接著在來(lái)分析一下findClass,通過(guò)分析findClass的代碼,最終會(huì)調(diào)用org.apache.catalina.loader.WebappClassLoader#findClassInternal方法,那我們就來(lái)分析一下它的代碼:

    [java]?view plaincopy
  • org.apache.catalina.loader.WebappClassLoader#findClassInternal??
  • [java]?view plaincopy
  • protected?Class<?>?findClassInternal(String?name)??
  • ????throws?ClassNotFoundException?{??
  • ??
  • ????//??
  • ????if?(!validate(name))??
  • ????????throw?new?ClassNotFoundException(name);??
  • ??
  • ????String?tempPath?=?name.replace('.',?'/');??
  • ????String?classPath?=?tempPath?+?".class";??
  • ??
  • ????ResourceEntry?entry?=?null;??
  • ??
  • ????if?(securityManager?!=?null)?{??
  • ????????PrivilegedAction<ResourceEntry>?dp?=??
  • ????????????new?PrivilegedFindResourceByName(name,?classPath);??
  • ????????entry?=?AccessController.doPrivileged(dp);??
  • ????}?else?{??
  • ????????//?1???
  • ????????entry?=?findResourceInternal(name,?classPath);??
  • ????}??
  • ??
  • ????if?(entry?==?null)??
  • ????????throw?new?ClassNotFoundException(name);??
  • ??
  • ????Class<?>?clazz?=?entry.loadedClass;??
  • ????if?(clazz?!=?null)??
  • ????????return?clazz;??
  • ??
  • ????synchronized?(this)?{??
  • ????????clazz?=?entry.loadedClass;??
  • ????????if?(clazz?!=?null)??
  • ????????????return?clazz;??
  • ??
  • ????????if?(entry.binaryContent?==?null)??
  • ????????????throw?new?ClassNotFoundException(name);??
  • ??
  • ????????try?{??
  • ????????????//?2??
  • ????????????clazz?=?defineClass(name,?entry.binaryContent,?0,??
  • ????????????????????entry.binaryContent.length,??
  • ????????????????????new?CodeSource(entry.codeBase,?entry.certificates));??
  • ????????}?catch?(UnsupportedClassVersionError?ucve)?{??
  • ????????????throw?new?UnsupportedClassVersionError(??
  • ????????????????????ucve.getLocalizedMessage()?+?"?"?+??
  • ????????????????????sm.getString("webappClassLoader.wrongVersion",??
  • ????????????????????????????name));??
  • ????????}??
  • ????????entry.loadedClass?=?clazz;??
  • ????????entry.binaryContent?=?null;??
  • ????????entry.source?=?null;??
  • ????????entry.codeBase?=?null;??
  • ????????entry.manifest?=?null;??
  • ????????entry.certificates?=?null;??
  • ????}??
  • ??
  • ????return?clazz;??
  • ??
  • }??
  • 上面的代碼標(biāo)注1(第19行)的地方通過(guò)名稱去當(dāng)前webappClassLoader的倉(cāng)庫(kù)中查找對(duì)應(yīng)的類文件,標(biāo)注2(第38行)的代碼,將找到的類文件通過(guò)defineClass轉(zhuǎn)變?yōu)镴vm可以識(shí)別的Class對(duì)象返回。

    總結(jié)

    以上是生活随笔為你收集整理的Tomcat源码解析六:Tomcat类加载器机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。