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