首先要说说Weblogic的classloading的机制(不同的Applicaiton Server,classloading的方式各有不同)。简而言之,weblogic默认情况下采用的是parent first的方式。但这个parent first,是有“讲究”(tricky)的。
1。父类加载器和子类加载器之间的关系类似于Java中,父类和子类之间的对象关系。
2。Weblogic会将所有load到的class缓存到cache中。(子类classloader能看到父类classloader加载到cache中的class)
默认情况下,当我们的应用程序(ear,war)运行时,会先去cache中查找class,如果找不到。就去System Classpath loader 里去找class。如果System Classpath loader里能找到你需要的类,那么不好意思,你在ear和war包里包含的class就没用了。
如果System ClassPath Loader找不到,接下来去ear的class path里找,接着去EJB class path里找,最后到war的class path里找。一旦找到了该类,就会load起这个类,并将该类放入cache中。
上面的描述,没什么奇怪,但需要注意的是下面的情况。当应用程序执行时,classloader需要的类还未在classloader里存在。默认情况下,此时classloader会由上至下从class path里找,也就是说先去System和Application的class path里找,而不是先向war的class path里找。所以,这种情况下,如果application的class path里能找到所需要的class,那么就算war的class path里有同样的class,war里的class是不会被load到的。
假设情况1:(只在webapp里有class A)
No Class A in current class loader cache -> Find System class path (Not found class A) -> Find Application class path (Not found class A) -> Find EJB class path (Not found class A) -> Find WebApp class loader (Found class A)
假设情况2:(在application和webapp里都有class A)
No Class A in current class loader cache -> Find System class path (Not found class A) -> Find Application class path (Found class A)
实际案例:
前几天,有同事用到一个第三方类库wsdl4j.jar,并该类库放在在Webapp的lib目录里。但是系统运行时,总是报类库版本不对的错误。问题就是在于,之前该项目在Application的class path里已经存在该类库了(给其他的war用),而且application class path里的类库和war里用的是不同的版本。
app.ear
|----->lib
|-->wsdl4j.jar
|------>a.war (using wsdl4j.jar in ear/lib)
|------>b.war (using wsdl4j.jar in ear/lib)
|------>c.war (using wsdl4j.jar in war/lib)
|---->WEB-INF
|---->lib
|--->wsdl4j.jar
解决方法:
weblogic提供了一个标签<prefer-web-inf-classes>,这个标签默认是false的,只要设置这个标签为true,就可以让WEB-INF里的类先被load到了。
特殊案例:(当第三方jar和weblogic.jar有冲突)
项目中使用CXF的webservice,CXF里有自己的javax.jws.*实现,而weblogic.jar里也有类似实现,Weblogic启动的时候似乎已经把weblogic.jar里的类都load进所谓的system classpath classloader了,程序在使用javax.jws.*的类时,类已经被system classpath classloader加载了,所以就算使用<prefer-web-inf-classes>标签也没有用。CXF总是用不上自己的javax.jws.*。
解决方法:
根据weblogic的官方文档,只要是在$CLASSPATH里的jar包都会在weblogic启动的时候load起来,存入“system classpath classloader”的cache里,所以程序运行时,classloader先从cache里找class,也就找到weblogic.jar里的javax.jws.*,所以永远不会尝试查找CXF里的javax.jws.*了。weblogic9以后提供了一个新的标签<prefer-application-packages>。用这个新标签能够让应用程序遇到javax.jws.*时,程序会直接到指定的ear的application class loader里找类,而不使用在“system classpath classloader”里的class。
大部分情况下,使用<prefer-web-inf-classes>,应该能解决classloading的问题,在<prefer-web-inf-classes>不生效的时候,就考虑使用<prefer-application-packages>。在网上还有人提到,同时使用这两个标签(一个在weblogic.xml里设,另一个在weblogic-application.xml在设)时,<prefer-web-inf-classes>的配置无效,这个问题还有待考证。
Reference:
http://chang.baidu.com/e_ville/snap/90dd96b459e4b1f74394861f.html
http://e-docs.bea.com/wls/docs100/programming/classloading.html#wp1082452
http://svn.apache.org/repos/asf/webservices/axis2/site/1_4/app_server.html