在上次的文章當中,把
eclipse做為web framework的核心,并且使得 servlet 的定義和mapping做為擴展點,確實狠
方便。不過現在又面臨一個問題,有個歷史遺留系統當中,有jsp 的應用。而jsp與servlet 的一個
很大的區別在于,它需要用 javac 去做編譯。這就使得問題復雜化了,特別是使得 class loader
的關系變得更復雜。我們首先看一下這里面有幾種 class loader,首先,啟動 eclipse 的是一個system loader,然后
是 eclipse starter 的 loader,啟動我們的核心class loader,這個核心負責啟動 jetty 和我們
的 web app兩個插件,每個插件都有自己的 eclipse bundler loader。而jetty插件啟動jetty
應用服務器,應用服務器本身有 context class loader,它還要負責去裝載 WEB-INF/lib
下的所有jar文件,以及 WEB-INF/classes 目錄下的文件,做為編譯期使用。
這里不僅class loader 眾多,而且關系復雜。一不小心就容易拋出 class not found 異常,或者是
class cast 異常。相對而言,只支持servlet 就狠簡單,因為只要把 servlet 的 context class
loader 用 eclipse bundler loader 替換掉就行。而jsp的編譯機制,導致了問題的出現。
我對于這種復雜classloader情況的心得,就是把錯綜復雜的 class loader 關系網拉直,變成一棵
樹。這樣的好處就是,對于loader 的關系比較清晰,出現ClassNotFoundException和
ClassCastException這兩種情況的時候,都狠容易判斷怎么回事,不會被繞暈。這種時候,單步跟蹤
只是找死,把classloader關系畫出來,有利于對問題的分析。
于是畫了這樣一個圖,把復雜的網拉直了,問題就迎刃而解了。

其中的關鍵就是 LauncherClassLoader。這個就是我們自己的ClassLoader,把它設置為 servlet
context classloader 的 parent,并且把 context classloader 的裝載順序改變成為先由parent
裝載,再自己裝載的模式。這樣,jsp的編譯還是由 context loader 去處理,我就不管了。其他的
該裝載的地方,還是有 eclipse bundler 去裝載,這樣問題基本解決。
不過這樣其實還是有個問題,如果要在 jsp 當中調用某個插件當中的 class,那么在編譯期就會出現
問題。不過由于目前只是解決 legacy jsp 應用的問題,所以這個暫時不存在。我能想到的解決方案
其實也狠簡單,把這些 jar 文件的url也做為擴展點出現,那么當jsp需要某個class時,只要把它
所需要的 jar 文件的 url 做為一個擴展,在 LauncherClassLoader 當中將這些jar文件添加到
url loader 當中,而這些 url 會出現在 jsp 的編譯class path當中。
主站:http://blogsite.3322.org/jspwiki/