java的classload机制--Tomcat 5
Tomcat是接触最久的应用服务器,同时也被它的classloading愚弄过好多次。印象中比较深的一次是建立了一个web应用,使用oracle数据库,我把oracle的jdbcdriver放到了WEB-INF/lib目录下面,然后给Tomcat配置了数据源,在这个应用里面连接这个数据源,来访问数据库。看起来一切正常,可是一启动Tomcat,就报出一个错误,说是找不到driver类。后来知道这是由于Tomcat的classloading机制造成的。总的来说,Tomcat开源、简单,文档清楚,又学习过一阵它的源码,是我了解最多的应用服务器了,所以就从它开始。
运行Tomcat就是运行org.apache.catalina.startup.Bootstrap类的main方法,和运行普通的应用程序并无二致,所以Java2的classloading机制适用于这个过程。但是Bootstrap运行起来以后,会加载common、server下面的类,加载webapps下面的web应用。这些类的加载是由不同的classloader来完成的。Tomcat的classloader结构如下:
BootStrapclassloader(加载JRE/lib下的rt.jar和其他重要jar文件)
↑
ExtClassLoader(加载JRE/lib/ext下的jar文件,Java扩展框架使用)
↑
AppClassLoader($CATALINA_HOME/bin/下bootstrap.jar,commons-logging-api.jar,commons-daemon.jar,$JAVA_HOME/lib/tools.jar,jmx.jar)
↑
common(加载$CATALINA_HOME/common/,Tomcat本身和WebApp共享类)
↑↑
Catalina(加载$CATALINA_HOME/server/,Tomcat本身使用的类)
Shared(加载$CATALINA_BASE/shared/,所有WebApp共享类)
↑
WebappClassLoader(加载各个WebApp的class,不同的WebApp被隔离开)
Tomcat在启动的时候,完全忽略了classpath的设置,而重新设置了classpath,所以AppClassLoader载入的类将不是classpath设置的类。
Tomcat没有完全使用Java2的parentdelegation模型。这一点体现在WebappClassLoader上。在一个webapp中,载入类的过程是这样的:
首先检查本地的WebappClassLoader,如果没有,
则请求它的父ClassLoader,即shared。
从shared开始,采用parentdelegation,即shared请求它的父classloadercommon来载入类,这个过程一直延续到BootStrapclassloader。
正是因为这种机制,使我们在两个Webapp中有相同的class的时候,不会相互干扰。比如说,两个app中都使用了log4j,在WEB-INF/lib下面分别有一份log4j.jar,配置输出到不同的文件。因为WebappClassLoader仅对本app可见,所以log4j可以独立工作,而不相互影响。但是,如果我们把这两个app下面的log4j.jar移动到shared目录或者common目录,那他们就会把日志输出到同样的文件了,因为这时候是共享的。
记得当时看到WebappClassLoader的这个特性,心下暗喜,盘算着自己能不能写一个java.lang.String类,放到WEB-INF/lib下面,而得到优先加载的机会呢?马上兴冲冲地进行试验,但是结果让我失望,翻出tomcat的源码一看,发现以java.,javax.,sun.,开头的class,WebappClassLoader一概不予理会,直接把烫山芋扔给它的父loader了。另外,Tomcat文档交待,遇到加载org.xml.sax.*,org.w3c.dom.*,org.apache.xerces.*,org.apache.xalan.*这些包的class的请求,WebappClassLoader也不会受理。
引用地址:
http://spaces.msn.com/myj1024/blog/