锁定老帖子 主题:JavaEE ear包类加载器机制解析
精华帖 (0) :: 良好帖 (5) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-12
最后修改:2009-04-12
在介绍EAR包的类加载器机制之前,我们需要了解一下JavaEE中都有哪些类型的包。 一 JavaEE 包的类型在J2EE中,有ejb-jar包,war包,rar包,car包,ear包,其中经常会用到ejb-jar包,war包,以及ear包,下面分别说明。 1 EJB Jar 包 (.jar)1.1 作用Ejb jar是EJB模块,主要用于实现业逻辑。 1.2 描述符文件EJB JAR包的部署描述符文件是ejb-jar.xml,(在EJB3.0中,也可以采用J2SE5.0引入的annoation注解,只不过ejb-jar.xml文件的内容会覆盖annoation) 1.3 内容EJB JAR包中通常包括会话bean(包括stateless session bean,statefull session bean),消息驱动bean(MDB),以及Entity bean(在EJB3.0中,采用新的JPA规范来进行数据库访问,所以不存在entity bean,所有的entity 都是pojo) 2 War 包 (.war)2.1 作用War包主要用于打包web应用程序。 2.2 描述符文件War包的描述符文件是web.xml,web.xml里可以配置相应的servlet,filter,listener等组件。 2.3 内容War包里主要包含jsp,servlet,filter,html,图片等资源。 3 Ear 包 (.ear)3.1 作用EAR包主要用于对JavaEE应用程序进行打包,这样方便交付给客户来使用。 3.2 描述符文件application.xml是ear包的描述符文件,application.xml中可以配置一个或者多个web模块,一个或者多个ejb模块,还可以配置资源适配器模块和应用客户端模块。 3.3 内容EAR包中包含ejb jar,war包,资源适配器模块(.rar)以及应用客户端模块。 二 JavaEE ear包的类加载机制1 委托模型在说ear包的类加载体系之前,首先我们需要知道java中的类加载器的委托模型,java中的类加载器有一个继承体系,子加载器在加载类的时候首先委托父加载器来加载,以此类推,如果所有的父加载器都不能加载,那么子加载器再加载,此时如果子加载器没有发现类文件,则抛出java.lang.ClassNotFoundException. 但是在JavaEE应用中,java默认的委托模型将会被禁用,此时加载类的时候,首先是子加载器加载类,如果子加载器找不到相应的类文件,那么委托给父加载器来加载。 2 JavaEE类加载机制2.1 java类加载器Java类加载器体系如下图所示:
2.2 JavaEE类加载器JavaEE中的类加载器是在上图的基础上实现的,如下图所示:
从上图中可以看出,application server类加载器是ejb类加载器的父加载器,ejb包的类加载器是war包的父加载器。 (注:上图只是大体上的类加载器体系,不同的application server有不同的实现,但是具体的原理是一样的)。 三:实战上面是关于类加载器的一些理论知识,下面通过一个具体的实例来验证以上理论。(以下实验均采用jboss 4.2 AS) 1 准备环境首先在eclipse建立三个工程,如图:
其中Demoejb工程包括两个文件,一个接口,一个实现类,Demoweb中包括一个DemoServlet类。其中我们编写一个测试类DemoUtil类。它里面包含一个静态变量,每次调用demo方法,都将count加一。具体的代码如下:
Demoear application.xml内容: <?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"> <display-name>Demoear</display-name> <module id="myeclipse.1238995486296"> <web> <web-uri>Demoweb.war</web-uri> <context-root>/Demoweb</context-root> </web> </module> <module id="myeclipse.1238994380625"> <ejb>Demoejb.jar</ejb> </module> </application>
DemoUtil 类:
package com.yuquan.util; /** * @author yuquan * @createTime Apr 11, 2009 3:47:45 PM */ public class DemoUtil { private static int count ; public static int demo() { return count++; } }
Demoejb project DemoService代码: package com.yuquan.service; import javax.ejb.Remote; /** * @author yuquan * @createTime Apr 6, 2009 1:08:41 PM */ @Remote public interface DemoService { public String execute(); public void print(); }
DemoServiceImpl代码: package com.yuquan.service; import javax.ejb.Remote; import javax.ejb.Stateless; import org.jboss.annotation.ejb.RemoteBinding; import com.yuquan.util.DemoUtil; /** * @author yuquan * @createTime Apr 6, 2009 1:10:24 PM */ @Stateless @Remote(DemoService.class) @RemoteBinding(jndiBinding="DemoServiceImpl") public class DemoServiceImpl implements DemoService { /* (non-Javadoc) * @see com.xmu.eartest.DemoService#execute() */ @Override public String execute() { return String.valueOf(DemoUtil.demo()); } @Override public void print() { ClassLoader loader = DemoUtil.class.getClassLoader(); while(loader !=null){ System.out.println("Demoservice print :::"+loader); loader = loader.getParent(); } } }
Demoweb project DemoServlet代码:
2 实验一2.1 结构Demoejb.jar: com/yuquan/service/DemoService.class com/yuquan/service/DemoServiceImpl.class com/yuquan/util/DemoUtil.class
Demoweb.war包 META-INF/ WEB-INF/classes/com/yuquan/action/DemoServlet.class WEB-INF/lib WEB-INF/web.xml Index.jsp
Demoear 包
此时DemoUtil.class打包到了Demoejb.jar包中,Demoweb.war包中没有DemoUtil类。 2.2 结果17:47:51,187 INFO [STDOUT] service classloader is : WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@d85409 17:47:51,187 INFO [STDOUT] DemoService classloader is : org.jboss.mx.loading.UnifiedClassLoader3@19a82ee{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34440Demoear.ear ,addedOrder=48} 17:47:51,187 INFO [STDOUT] After the web being invoked, the static value is : 0 17:47:51,203 INFO [STDOUT] After the ejb being invoked, the static value is : 1 17:47:51,203 INFO [STDOUT] Ejb print 17:47:51,203 INFO [STDOUT] --------------------- 17:47:51,203 INFO [STDOUT] Demoservice print :::org.jboss.mx.loading.UnifiedClassLoader3@19a82ee{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34440Demoear.ear ,addedOrder=48} 17:47:51,203 INFO [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 17:47:51,203 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f 17:47:51,203 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0 17:47:51,203 INFO [STDOUT] web print 17:47:51,203 INFO [STDOUT] --------------------- 17:47:51,203 INFO [STDOUT] DemoServlet print ::: org.jboss.mx.loading.UnifiedClassLoader3@19a82ee{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34440Demoear.ear ,addedOrder=48} 17:47:51,203 INFO [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 17:47:51,203 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f 17:47:51,203 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$ExtClassLoader@360be0
从运行的结果可以看出war包以及ejb包中用到得DemoUtil类都是由jboss的org.jboss.mx.loading.UnifiedClassLoader3来加载的。我们看到DemoUtil类的count静态域变为了1,这是因为DemoServlet用到得DemoUtil类,其实是由ejb 包加载器UnifiedClassLoader3加载的,所以ejb,web调用后,count值变为了1.这也就说明了UnifiedClassLoader3类加载器是war包类加载器(org.apache.catalina.loader.WebappClassLoader)的父加载器.
3实验二3.1 结构实验二中,我们将Demoejb.jar放到Demoweb.war包的lib目录下,其它的和实验一一样。 3.2 结果18:00:49,609 INFO [STDOUT] service classloader is : WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@7a8ba4 18:00:49,609 INFO [STDOUT] DemoService classloader is : WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@7a8ba4 18:00:49,609 INFO [STDOUT] After the web being invoked, the static value is : 0 18:00:49,625 INFO [STDOUT] After the ejb being invoked, the static value is : 0 18:00:49,625 INFO [STDOUT] Ejb print 18:00:49,625 INFO [STDOUT] --------------------- 18:00:49,625 INFO [STDOUT] Demoservice print :::org.jboss.mx.loading.UnifiedClassLoader3@1d2052b{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34441Demoear.ear ,addedOrder=49} 18:00:49,625 INFO [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 18:00:49,625 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f 18:00:49,656 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0 18:00:49,656 INFO [STDOUT] web print 18:00:49,656 INFO [STDOUT] --------------------- 18:00:49,656 INFO [STDOUT] DemoServlet print ::: WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@7a8ba4 18:00:49,656 INFO [STDOUT] DemoServlet print ::: java.net.FactoryURLClassLoader@7a8ba4 18:00:49,656 INFO [STDOUT] DemoServlet print ::: org.jboss.mx.loading.UnifiedClassLoader3@1d2052b{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34441Demoear.ear ,addedOrder=49} 18:00:49,656 INFO [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 18:00:49,656 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f 18:00:49,656 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$ExtClassLoader@360be0
从运行的结构可以看出,count的值在ejb,web调用后的值都是0,此时DemoUtil在war包和ejb包中的类加载器是不一样的,这也就说明了在JavaEE应用中,web包的类加载器首先加载,如果没有找到相应的class文件,那么再委托给父加载器(ejb包的类加载器)来加载。并且此时注意到Demoservice也是由web类加载器加载的,这是因为此时Demoejb.jar被放在了Demoweb.war包的lib目录,war包类加载器可以找到此类,所以由war包类加载器来加载此类。但是这个时候要注意ejb包中的DemoService类还是由Ejb包的类加载器来加载的,因为此时web类加载器是子加载器,做为父加载器的ejb类加载器是看不到子加载器加载的类的。 从这个例子,我们得出两个个结论: 1)war包类加载器在加载类的时候,首先在自己对应的路劲中查找类(WEB-INF/class,WEB-INF/lib,以及lib包 jar文件META-INF/MANIFEST.MF classpath指定的jar),如果找不到才会委托给父加载器(ejb包类加载器)加载,以此类推,如果所有的父加载器都不能加载,那么就抛出java.lang.ClassNotFoundException. 2)父加载器看不到子加载器加载的类,本例中war包中用到的类加载器加载了DemoService,但是ejb包的类加载器也加载了相应的DemoService类。
4 实验三:4.1 结构实验三中,我们将Demoejb.jar包中的DemoUtil.类删除,将其打包到独立的util.jar包中,然后将util.jar包放到Demoweb.war包的lib目录下面,并且同时也需要把util.jar包放到Demoear.ear包的lib目录下。 4.2 结果18:07:51,343 INFO [STDOUT] service classloader is : WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@14322ba 18:07:51,343 INFO [STDOUT] DemoService classloader is : org.jboss.mx.loading.UnifiedClassLoader3@133650d{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34442Demoear.ear ,addedOrder=50} 18:07:51,343 INFO [STDOUT] After the web being invoked, the static value is : 0 18:07:51,343 INFO [STDOUT] After the ejb being invoked, the static value is : 0 18:07:51,343 INFO [STDOUT] Ejb print 18:07:51,343 INFO [STDOUT] --------------------- 18:07:51,343 INFO [STDOUT] Demoservice print :::org.jboss.mx.loading.UnifiedClassLoader3@133650d{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34442Demoear.ear ,addedOrder=50} 18:07:51,343 INFO [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 18:07:51,343 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f 18:07:51,343 INFO [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0 18:07:51,343 INFO [STDOUT] web print 18:07:51,343 INFO [STDOUT] --------------------- 18:07:51,343 INFO [STDOUT] DemoServlet print ::: WebappClassLoader delegate: false repositories: /WEB-INF/classes/ ----------> Parent Classloader: java.net.FactoryURLClassLoader@14322ba 18:07:51,343 INFO [STDOUT] DemoServlet print ::: java.net.FactoryURLClassLoader@14322ba 18:07:51,343 INFO [STDOUT] DemoServlet print ::: org.jboss.mx.loading.UnifiedClassLoader3@133650d{ url=file:/E:/java/file resource/jboss-4.2.1.GA/jboss-4.2.1.GA/server/default/tmp/deploy/tmp34442Demoear.ear ,addedOrder=50} 18:07:51,343 INFO [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d 18:07:51,343 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f 18:07:51,343 INFO [STDOUT] DemoServlet print ::: sun.misc.Launcher$ExtClassLoader@360be0
从运行结果来看,ejb包中所用的DemoUtil类是有jboss的UnifiedClassLoader3加载的,而DemoServlet中用到得DemoUtil类是由WebAppClassLoader加载的。 注意:如果此时在Demoear.ear的lib包中不放置util.jar包,那么EJB中将无法加载到此类,这也说明了父加载器是看不到子加载器加载的类的。 四:结论1 子加载器可以看到父加载器加载的类,但是父加载器看不到子加载器加载的类,比如实验一中,DemoServlet中用到得DemoService类就是由org.jboss.mx.loading.UnifiedClassLoader加载的。 2 同级的类加载是不能看到对方加载的类的。假如ear包中包括了很多个war包,这些war包中的类是不能互相引用的。 3 java的默认委托模型在JavaEE 应用的类加载器模型中不再适用。此时首先有war包的类加载加载类,如果war包类加载器不能加载,然后才由ejb包的类加载来加载。 4 jboss4.2 AS中,类加载器的体系如下: org.apache.catalina.loader.WebappClassLoader java.net.FactoryURLClassLoader org.jboss.mx.loading.UnifiedClassLoader3 org.jboss.system.server.NoAnnotationURLClassLoader sun.misc.Launcher$AppClassLoader sun.misc.Launcher$ExtClassLoader 以上的classLoader中,下面的类加载器是上面的父加载器.
需要注意一下单例设计模式。如果我们把一个类设计为单例的,要确保web模块和ejb模块中用到得单例类是同一个类加载器加载的,否则的话web模块和ejb模块的实例是不一样的。 最后,一般我们在打ear包的时候,会把web模块和ejb模块都用到得类放到ear包的lib目录下,这样确保公用类是同一个类加载器加载。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-04-15
上面漏掉了两点结论:(因为编辑,帖子格式会变乱,就补充在这里)
1 Ear包中的所有Ejb 模块都共享同一个classloader. 2 Ear包中的每个web模块都会有对应的一个classloader. 所以ejb模块中的类对web模块是可见的,反之则不成立,同时不同的web模块之间看不到对方classloader已经加载的类。 |
|
返回顶楼 | |
发表时间:2009-06-11
不是绝对的JEE应用服务器都是用parent last模式,WebSphere默认的还是parent first模式,可以根据自己的需要改成Parent last模式。
|
|
返回顶楼 | |
发表时间:2009-06-11
swingfish 写道 不是绝对的JEE应用服务器都是用parent last模式,WebSphere默认的还是parent first模式,可以根据自己的需要改成Parent last模式。
恩,多谢,我是基于jboss app server分析的。 |
|
返回顶楼 | |
浏览 9092 次