Robbin关于App class loader的总结
http://forum.javaeye.com/viewtopic.php?t=11
Java本身是一种设计的非常简单,非常精巧的语言,所以Java背后的原理也很简单,归结起来就是两点:
1、JVM的内存管理
理解了这一点,所有和对象相关的问题统统都能解决
2、JVM Class Loader
理解了这一点,所有和Java相关的配置问题,包括各种App Server的配置,应用的发布问题统统都能解决
App Class Loader
|----- EJB Class Loader
|----- Web App Class Loader
如果在App Class Loader级别配置,是全局可见的。如果打包在EJB里面,那么就不会影响到Web Application,反之亦然,如果你在WEB-INF下面放置Hibernate,也不会影响到EJB。放在EJB Class Loader或者放在Web App Class Loader级别主要就是在局部范围内有效,不影响到其它的应用。
试想,如果在一个Weblogic上面配置多个虚拟域,你使用www.bruce.com域名,开发你的网站,我使用www.fankai.com开发我的网站,那么当然不希望我们的Hibernate相互干扰,所以就可以放在 EJB Class Loader级别来配置Hibernate。
进一步阐述一下EJB Class Loader的问题:
先再次强调一下,Hibernate和EJB,和App Server不存在兼容性问题,他们本来就是不相关的东西,就好像JDBC,相信没有人会认为JDBC和EJB不兼容吧,Hibernate也是一样,它只和JDBC驱动,和数据库有兼容性问题,而和EJB,和App Server完全是不搭界的两回事。凡是认为Hibernate和EJB不兼容的人,其实是都是因为对EJB学习的不到家,把责任推到Hibernate身上了。
我前面的帖子提到过Class Loader的层次,这里不重复了,总之我们先来看看Class 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上运行,就可以互相不干扰。
A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system.
Every Class object contains a reference to the ClassLoader that defined it.
Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime. The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
Applications implement subclasses of ClassLoader in order to extend the manner in which the Java virtual machine dynamically loads classes.
Class loaders may typically be used by security managers to indicate security domains.
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, on UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH environment variable.
However, some classes may not originate from a file; they may originate from other sources, such as the network, or they could be constructed by an application. The method defineClass converts an array of bytes into an instance of class Class. Instances of this newly defined class can be created using Class.newInstance.
The methods and constructors of objects created by a class loader may reference other classes. To determine the class(es) referred to, the Java virtual machine invokes the loadClass method of the class loader that originally created the class.
For example, an application could create a network class loader to download class files from a server. Sample code might look like:
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .
The network class loader subclass must define the methods findClass and loadClassData to load a class from the network. Once it has downloaded the bytes that make up the class, it should use the method defineClass to create a class instance. A sample implementation is:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
Java本身是一种设计的非常简单,非常精巧的语言,所以Java背后的原理也很简单,归结起来就是两点:
1、JVM的内存管理
理解了这一点,所有和对象相关的问题统统都能解决
2、JVM Class Loader
理解了这一点,所有和Java相关的配置问题,包括各种App Server的配置,应用的发布问题统统都能解决
App Class Loader
|----- EJB Class Loader
|----- Web App Class Loader
如果在App Class Loader级别配置,是全局可见的。如果打包在EJB里面,那么就不会影响到Web Application,反之亦然,如果你在WEB-INF下面放置Hibernate,也不会影响到EJB。放在EJB Class Loader或者放在Web App Class Loader级别主要就是在局部范围内有效,不影响到其它的应用。
试想,如果在一个Weblogic上面配置多个虚拟域,你使用www.bruce.com域名,开发你的网站,我使用www.fankai.com开发我的网站,那么当然不希望我们的Hibernate相互干扰,所以就可以放在 EJB Class Loader级别来配置Hibernate。
进一步阐述一下EJB Class Loader的问题:
先再次强调一下,Hibernate和EJB,和App Server不存在兼容性问题,他们本来就是不相关的东西,就好像JDBC,相信没有人会认为JDBC和EJB不兼容吧,Hibernate也是一样,它只和JDBC驱动,和数据库有兼容性问题,而和EJB,和App Server完全是不搭界的两回事。凡是认为Hibernate和EJB不兼容的人,其实是都是因为对EJB学习的不到家,把责任推到Hibernate身上了。
我前面的帖子提到过Class Loader的层次,这里不重复了,总之我们先来看看Class 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上运行,就可以互相不干扰。
A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system.
Every Class object contains a reference to the ClassLoader that defined it.
Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime. The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
Applications implement subclasses of ClassLoader in order to extend the manner in which the Java virtual machine dynamically loads classes.
Class loaders may typically be used by security managers to indicate security domains.
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, on UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH environment variable.
However, some classes may not originate from a file; they may originate from other sources, such as the network, or they could be constructed by an application. The method defineClass converts an array of bytes into an instance of class Class. Instances of this newly defined class can be created using Class.newInstance.
The methods and constructors of objects created by a class loader may reference other classes. To determine the class(es) referred to, the Java virtual machine invokes the loadClass method of the class loader that originally created the class.
For example, an application could create a network class loader to download class files from a server. Sample code might look like:
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .
The network class loader subclass must define the methods findClass and loadClassData to load a class from the network. Once it has downloaded the bytes that make up the class, it should use the method defineClass to create a class instance. A sample implementation is:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}