一.ClassLoader 在应用服务器中的使用 每一种应用服务器都有自己的Class Loader,而且各个Class Loader的实现方式和层次结构都不太相同。因此在使用和部署应用的时候,需要先了解当前应用服务器的Class Loader的特点,才能知道哪些类库文件应该放到哪个目录下。
总的来说,应用服务器使用class loader的主要目的是为了安全和效率。安全是通过Class Loader将不同的应用互相隔离,使得不同的应用可以有相同的类名,而不互相干扰。效率是通过代理(delegate)模式来使得各个不同的应用如果使 用相同的类库,只在内存中装载一次,节省装载的时间和对内存的开销。
二.问题出现的原因
正是因为代理的模式,导致了上面的问题:在 JES Application Server本身实现中已经使用了大量开源的软件包,例如(org.apache.commons)的各个工具包。在代理模式下,各个Web应用会把类装 载的工作代理给它的上级或上级的上级等等。如果当JES Application Server本身已经装载这些包,那就不会再装载应用中的相同名字的类库了。当应用使用的类库与JES使用的类库有版本冲突的时候,就会造成一些麻烦。
二. JES上的解决方案
在Web应用的部署过程中,只需要在sun-web.xml描述文件中,加上下面的属性值,就能关闭代理的模式,解决上面的问题。
<class-loader>
<property delegate=false/>
</class-loader>
三. JBoss上的解决方案在EAR应用的部署过程中,只需要在METE_INF/jboss-app.xml描述文件中,加入以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-app>
<loader-repository loaderRepositoryClass='org.jboss.mx.loading.HeirarchicalLoaderRepository3'>
sso:loader=sso.ear
<loader-repository-config configParserClass='org.jboss.mx.loading.HeirarchicalLoaderRepository3ConfigParser '>
java2ParentDelegaton=true
</loader-repository-config>
</loader-repository>
</jboss-app>
HeirarchicalLoaderRepository3的作用是首先在EAR内部查找class,java2ParentDelegaton=true的意思是找不到就委托给父ClassLoader,设为false就会抛出ClassNotFound异常。
四. App ClassLoader原理此部分内容转贴自:
http://www.javaeye.com/article/11Class Loader的作用范围:
BootStrap Class Loader:
load JRE\lib\rt.jar, sunrsasign.jar, charsets.jar, jce.jar, jsse.jar, plugin.jar
Ext Class Loader:
load JRE\lib\ext目录下的库文件, load JRE\classes目录下的类
App Class Loader:
load CLASSPATH变量指定路径下的类
以上的load路径都是写死在JVM的C++源代码里面的,不能改变,详细请见王森的《Java深度历险》
在一个特定的App Server上,Class Loader会继续向下继承,继承的层次会根据不同的App Server有所不同,但是肯定不会变的就是:
EJB Class Loader:
继承自App Class Loader,继承层次根据App Server有所不同,一个EJB Class Loader它的load Class的范围仅限于JAR或者EAR范围之内。
Web App Class Loader:
继承自App Class Loader,继承层次根据App Server有所不同,一个Web App Class Loader:它的load Class的范围在 WEB-INF\lib下的库文件和WEB-INF\classes目录下的class文件。
Web App Class Loader很好理解,大家毕竟用的很多,App Server上的一个Web Application会创建一个Web App Class Loader的实例去负责load class,所以如果你想让Hibernate只在这个Web Application内生效,把它放到WEB-INF\lib下去就好了。
如果你把Hibernate放到了CLASSPATH变量指定的路径下,而你在WEB-INF\lib也放了一份,那么Web App Class Loader由于load范围所限,它会首先找到WEB-INF\lib下的那份Hibernate,按照它的配置来初始化Hibernate。
如果你把Hibernate放到了CLASSPATH变量指定的路径下,但你在WEB-INF\lib什么都没有放,那么Web App Class Loader由于load范围所限,它根本什么都找不到,于是它把load Hibernate的责任交给上一级的Class Loader,这样直到App Class Loader,它找到了Hibernate,按照它的配置来初始化Hibernate。
EJB Class Loader稍微复杂一点,不那么容易理解。App Server会针对每一个EJB包文件创建一个EJB Class Loader的实例,例如:
HelloRobbin.jar
HelloBruce.jar
当你把这两个jar发布到App Server上以后,会创建两个EJB Class Loader的实例,分别去load这两个EJB包,比如说:
CLEJB_Robbin是load HelloRobbin.jar的
CLEJB_Bruce是load HelloBruce.jar的
那么CLEJB_Robbin的load范围就仅仅限于HelloRobbin.jar之内,它load不到HelloRobbin.jar之外的任何文件,当然它也load不到HelloBruce.jar。
说到这里,我相信大家应该已经明白为什么EJB规范不允许EJB有IO操作了吧?因为EJB Class Loader根本找不到jar包之外的文件!!!
如果现在你想实现HelloRobbin.jar和HelloBruce.jar的互相调用,那么该怎么办?他们使用了不同的EJB Class Loader,相互之间是找不到对方的。解决办法就是使用EAR。
现在假设HelloRobbin.jar和HelloBruce.jar都使用了Hibernate,看看该怎么打包和发布:
HelloEJB.ear
|------ HelloRobbin.jar
|------ HelloBruce.jar
|------ Hibernate2.jar
|------ pojo.jar (定义所有的持久对象和hbm文件的jar包)
|------ cglib-asm.jar
|------ commons-beanutils.jar
|------ commons-collections.jar
|------ commons-lang.jar
|------ commons-logging.jar
|------ dom4j.jar
|------ odmg.jar
|------ log4j.jar
|------ jcs.jar
|------ hibernate.properties
|------ log4j.properties
|------ cache.ccf
|------ META-INF\application.xml (J2EE规范的要求,定义EAR包里面包括了哪几个EJB)
除此之外,按照EJB规范要求,HelloRobbin.jar和HelloBruce.jar还必须指出调用jar包之外的类库的名称,这需要在jar包的manifest文件中定义:
HelloRobbin.jar
|------ META-INF\MANIFEST.MF
MANIFEST.MF中必须包括如下一行:
Class-Path: log4j.jar hibernate2.jar cglib-asm.jar commons-beanutils.jar commons-collections.jar commons-lang.jar
commons-logging.jar dom4j.jar jcs.jar odmg.jar jcs.jar pojo.jar
这样就OK了,当把HelloEJB.ear发布到App Server上以后,App Server创建一个EJB Class Loader实例load EAR包里面的EJB,再根据EJB的jar包里面的MANIFEST.MF指出的Class-Path去寻找相应的jar包之外的类库。
所以一个EAR包有点类似一个Web Application,EJB Class Loader的load范围也就是EAR范围之内,它load不到EAR之外的文件。除非把Hibernate定义到CLASSPATH指定的路径下,在 这种情况下,EJB Class Loader找不到Hibernate,只能交给上一级的Class Loader,最后由App Class Loader找到Hibernate,进行初始化。
由于EAR这样load Class规则,假设Robbin和Bruce都在同一个Weblogic上运行自己的网站,而我们都不希望自己的程序里面的Hibernate配置被对方的搞乱掉,那么我们就可以这样来做:
Robbin's Website:
Robbin.ear
|-------- robbin.war (把Web Application打包)
|-------- robbin.jar (把开发的EJB打包)
|-------- Hibernate2.jar
..........................
|-------- META-INF\application.xml
Bruce's Website:
Bruce.ear
|-------- bruce.war (把Web Application打包)
|-------- bruce.jar (把开发的EJB打包)
|-------- Hibernate2.jar
..........................
|-------- META-INF\application.xml
这样在同一个App Server上运行,就可以互相不干扰。