最近在做一個項目,綜合使用了jboss的microcontainer,jetty和自己定義的war包,war包中還會用到spring,因為牽涉到多個容器,同時又有自己的自定義類,所以classloader環(huán)境異常復(fù)雜,ClassNotFound問題搞得頭都大了,最后綜合各種因素,設(shè)計了如下的一個classloader層次:
其中,紅色部分是系統(tǒng)(也就是啟動java程序加載Main函數(shù)的classloader),主要的設(shè)計考量有以下幾點:
1、使用自定義的ExtClassLoader(加載java的ext目錄下的jar包)把程序加載的class完全和系統(tǒng)加載的class隔離開,這樣即使在eclipse容器中啟動都不會有類沖突。
為什么不從系統(tǒng)的ExtClassLoader作為自定義classloader數(shù)的根有兩個考慮,第一個是系統(tǒng)ExtClassLoader有可能不存在,第二個就是如果使用同一個ExtClassLoader中,在處理JNDI、XML和URL解析等java擴展功能時會遇到后加載的handler部分導(dǎo)致不同classloader樹加載的同一個類的ClassCastException,具體參見這些模塊的源代碼。
2、WarClassLoader除了系統(tǒng)類和Common類(目前只有l(wèi)og相關(guān)類)以外的類都從war包的WEB-INFO和classes下加載。
3、所有執(zhí)行War包中代碼的線程ThreadContextClassLoader都設(shè)置為WarClassLoader,以供Spring和Webx中的相關(guān)工具類使用這個classloader結(jié)構(gòu)的后門來加載war包中的類,典型例子是Webx中ResourceLoaderService就是使用ContextClassLoader來加載類的。
4、RialtoClassLoader也就是這個項目的容器加載器和WarClassLoader不在同一個樹路徑上,可以避免程序使用類和war使用類的class沖突,典型的是Spring容器相關(guān)代碼。
5、CommonClassLoader加載的類需要嚴(yán)格控制,否則可能會導(dǎo)致運行期類沖突,例如Spring的相關(guān)jar包絕對不可以出現(xiàn)在這個classloader作用范圍內(nèi)。
總之,ClassLoader采用父分派機制,后來增加的Thread ContextClassLoader在這個體系上增加了一個后門,帶來了靈活性,也帶來了很多令人困擾的問題,在做容器類的項目時難免會遇到class loader層次設(shè)計的問題,這里拋磚引玉,歡迎達人拍磚。