? ? ? ?大家都知道俊嗽,Java的類(lèi)加載機(jī)制是雙親委派模型墨辛,那么什么是雙親委派模型呢瘪吏?我們這里簡(jiǎn)要的說(shuō)一下鞍历,雙親委派模型就是說(shuō)把類(lèi)的加載委托給它的父類(lèi)加載器去加載,父加載器委托給它的祖父加載器去加載肪虎,一直這樣,直到它的父加載器是null為止惧蛹,類(lèi)加載器有如下的幾種類(lèi)型:
類(lèi)加載器的源代碼如下扇救,從中,我們可以很清楚的看到類(lèi)加載委派的過(guò)程:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//這里就是委派過(guò)程香嗓,直到父類(lèi)加載器為null為止
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
? ? ? ?好了迅腔,Java的類(lèi)加載機(jī)制就說(shuō)道這里,畢竟我們這里是看看tomcat的類(lèi)載入器靠娱,下面進(jìn)入主題沧烈。參考書(shū)籍(深入剖析tomcat)。
? ? ? ?tomcat必須實(shí)現(xiàn)一個(gè)自己的類(lèi)加載器像云,這是因?yàn)槲覀儾⒉荒芡耆湃蝧ervlet容器锌雀,他應(yīng)該只能加載自己WEB-INF/classes目錄下的類(lèi)和部署到WEB-INF/lib中的類(lèi)蚂夕,而在tomcat中,這個(gè)類(lèi)必須實(shí)現(xiàn)org.apache.catalina.Loader腋逆。先來(lái)看看Loader接口的定義:
public interface Loader {
//這個(gè)方法在后面說(shuō)
public void backgroundProcess();
//獲取ClassLoader實(shí)例
public ClassLoader getClassLoader();
//獲取Context
public Context getContext();
public void setContext(Context context);
public boolean getDelegate();
//指明是否要委托給一個(gè)父類(lèi)加載器
public void setDelegate(boolean delegate);
public boolean getReloadable();
public void setReloadable(boolean reloadable);
public void addPropertyChangeListener(PropertyChangeListener listener);
//獲取tomcat里的類(lèi)是否發(fā)生了修改
public boolean modified();
public void removePropertyChangeListener(PropertyChangeListener listener);
}
? ? ? ?我們主要看modified()方法婿牍,這個(gè)方法是用來(lái)獲取tomcat里的類(lèi)是否發(fā)生了修改,我們都知道惩歉,我們可以通過(guò)配置來(lái)修改tomcat是否支持熱啟動(dòng)等脂,那么它的原理是什么呢?它通過(guò)一個(gè)線(xiàn)程來(lái)周期性的調(diào)用modified()方法撑蚌,來(lái)確定類(lèi)是否發(fā)生了修改上遥,如果發(fā)生了,那么就會(huì)調(diào)用context的reload方法來(lái)自動(dòng)重載争涌,我們可以去org.apache.catalina.core.ContainerBase類(lèi)中找到run()方法粉楚,其由內(nèi)部類(lèi)ContainerBackgroundProcessor實(shí)現(xiàn),它周期的調(diào)用org.apache.catalina.core.StandardContext下的backgroundProcess()方法第煮,而這個(gè)backgroundProcess 方法也是Loader接口中聲明的方法解幼,這個(gè)方法如下所示:
@Override
public void backgroundProcess() {
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader(WebappLoader.class.getClassLoader());
if (context != null) {
//發(fā)生了修改,這調(diào)用context的reload方法包警,重新加載容器
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
}
}
}
}
? ? ? ?至此撵摆,可以看到tomcat是如何支持熱啟動(dòng)的。
? ? ? ?在tomcat中害晦,負(fù)責(zé)載入類(lèi)的是WebappClassLoader特铝,它的父類(lèi)中定義了一些不能載入的類(lèi),比如以javax開(kāi)頭的類(lèi)壹瘟,具體可以到WebappClassLoaderBase類(lèi)中的filter方法中查看鲫剿。當(dāng)然,為了達(dá)到更好的性能稻轨,會(huì)緩存已經(jīng)載入的類(lèi)灵莲,下次使用的時(shí)候就可以直接獲取。
? ? ? ?在載入類(lèi)的時(shí)候殴俱,WebappClassLoader會(huì)遵守如下的規(guī)則:
? ? ? ?1)因?yàn)樗幸呀?jīng)載入的類(lèi)都會(huì)緩存起來(lái)政冻,所以載入類(lèi)時(shí)要先檢查本地緩存;
? ? ? ?2)若本地緩存中沒(méi)有线欲,則檢查上一層緩存明场,即調(diào)用 java.lang.ClassLoader 類(lèi)的findLoadedClass() 方法;
? ? ? ?3)若兩個(gè)緩存中都沒(méi)有李丰,則使用系統(tǒng)的類(lèi)載入器進(jìn)行加載苦锨,防止 web 應(yīng)用程序中的類(lèi)覆蓋J2EE 的類(lèi);
? ? ? ?4)若啟用了 SecurityManager,則檢查是否允許載入該類(lèi)舟舒。若該類(lèi)是禁止載入的類(lèi)拉庶,拋出 ClassNotFoundException異常;
? ? ? ?5)若打開(kāi)標(biāo)志位 delegate魏蔗,或者待載入的類(lèi)是屬于包觸發(fā)器中的包名砍的,則調(diào)用父載入器來(lái)載入相關(guān)類(lèi)。如果父載入器是null莺治,則使用系統(tǒng)的類(lèi)載入器廓鞠;
? ? ? ?6)從當(dāng)前倉(cāng)庫(kù)中載入相關(guān)的類(lèi);
? ? ? ?7)若當(dāng)前倉(cāng)庫(kù)中沒(méi)有需要的類(lèi)谣旁,且標(biāo)志位delegate關(guān)閉床佳,則使用父類(lèi)載入器。若父類(lèi)載入器為 null榄审, 則使用系統(tǒng)的類(lèi)載入器進(jìn)行加載砌们;
? ? ? ?8)若仍未找到需要的類(lèi),則拋出 ClassNotFoundException 異常搁进;
? ? ? ?至此浪感,結(jié)束。