Tomcat:正統(tǒng)的類加載器架構(gòu)

Tomcat類加載器說明
前面3個類加載和默認(rèn)的一致,CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader則是Tomcat自己定義的類加載器,它們分別加載/common/、/server/、/shared/(在tomcat 6之后已經(jīng)合并到根目錄下的lib目錄下)和/WebApp/WEB-INF/中的Java類庫。其中WebApp類加載器和Jsp類加載器通常會存在多個實例,每一個Web應(yīng)用程序?qū)?yīng)一個WebApp類加載器,每一個JSP文件對應(yīng)一個Jsp類加載器。
commonLoader:Tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個Webapp訪問;
catalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對于Webapp不可見;
sharedLoader:各個Webapp共享的類加載器,加載路徑中的class對于所有Webapp可見,但是對于Tomcat容器不可見;
WebappClassLoader:各個Webapp私有的類加載器,加載路徑中的class只對當(dāng)前Webapp可見;
從圖中的委派關(guān)系中可以看出:CommonClassLoader能加載的類都可以被Catalina ClassLoader和SharedClassLoader使用,從而實現(xiàn)了公有類庫的共用,而CatalinaClassLoader和Shared ClassLoader自己能加載的類則與對方相互隔離。
WebAppClassLoader可以使用SharedClassLoader加載到的類,但各個WebAppClassLoader實例之間相互隔離。
而JasperLoader的加載范圍僅僅是這個JSP文件所編譯出來的那一個.Class文件,它出現(xiàn)的目的就是為了被丟棄:當(dāng)Web容器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例,并通過再建立一個新的Jsp類加載器來實現(xiàn)JSP文件的HotSwap功能。
Tomcat 如果使用默認(rèn)的類加載機制行不行?
我們思考一下:Tomcat是個web容器, 那么它要解決什么問題:
- 一個web容器可能需要部署兩個應(yīng)用程序,不同的應(yīng)用程序可能會依賴同一個第三方類庫的不同版本,不能要求同一個類庫在同一個服務(wù)器只有一份,因此要保證每個應(yīng)用程序的類庫都是獨立的,保證相互隔離。
- 部署在同一個web容器中相同的類庫相同的版本可以共享。否則,如果服務(wù)器有10個應(yīng)用程序,那么要有10份相同的類庫加載進虛擬機,這是扯淡的。
- web容器也有自己依賴的類庫,不能于應(yīng)用程序的類庫混淆?;诎踩紤],應(yīng)該讓容器的類庫和程序的類庫隔離開來。
- web容器要支持jsp的修改,我們知道,jsp 文件最終也是要編譯成class文件才能在虛擬機中運行,但程序運行后修改jsp已經(jīng)是司空見慣的事情,否則要你何用? 所以,web容器需要支持 jsp 修改后不用重啟。
Tomcat如果使用默認(rèn)的類加載機制行不行?
答案是不行的。為什么?
第一個問題,如果使用默認(rèn)的類加載器機制,那么是無法加載兩個相同類庫的不同版本的,默認(rèn)的累加器是不管你是什么版本的,只在乎你的全限定類名,并且只有一份。
第二個問題,默認(rèn)的類加載器是能夠?qū)崿F(xiàn)的,因為他的職責(zé)就是保證唯一性。
第三個問題和第一個問題一樣。
我們再看第四個問題,我們想我們要怎么實現(xiàn)jsp文件的HotSwap,jsp 文件其實也就是class文件,那么如果修改了,但類名還是一樣,類加載器會直接取方法區(qū)中已經(jīng)存在的,修改后的jsp是不會重新加載的。那么怎么辦呢?我們可以直接卸載掉這jsp文件的類加載器,所以你應(yīng)該想到了,每個jsp文件對應(yīng)一個唯一的類加載器,當(dāng)一個jsp文件修改了,就直接卸載這個jsp類加載器。重新創(chuàng)建類加載器,重新加載jsp文件。
字節(jié)碼生成技術(shù)與動態(tài)代理的實現(xiàn)
- 動態(tài)代理
public class DynamicProxyTest{
interface IHello{
void sayHello();
}
static class Hello implements IHello{
@Override
public void sayHello(){
System.out.println("Hello class say hello")
}
}
static class DynamicProxy implements InvokecationHandler{
Object originalObj;
Object bind(Object originalObj){
this.originalObj=originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),originalObj.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy,Method method,Object[]args)throw Throwable{
System.out.println("welcome");
return method.invoke(originalObj,args);
}
public static void main(String []args){
IHello hello=(IHello) new DynamicProxy().bind(new Hello());
hello.sayHello();
}
}
}
----
welcome
Hello Class say hello