在上次的文章当中,把
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/