在上文中,已經(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ō)一下吧。
- 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);
}
?
更多文章、技術(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ì)您有幫助就好】元

