JavaEE ear包类加载器机制解析

在介绍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包的类加载机制

委托模型

    在说ear包的类加载体系之前,首先我们需要知道java中的类加载器的委托模型,java中的类加载器有一个继承体系,子加载器在加载类的时候首先委托父加载器来加载,以此类推,如果所有的父加载器都不能加载,那么子加载器再加载,此时如果子加载器没有发现类文件,则抛出java.lang.ClassNotFoundException.

但是在JavaEE应用中,java默认的委托模型将会被禁用,此时加载类的时候,首先是子加载器加载类,如果子加载器找不到相应的类文件,那么委托给父加载器来加载。

2 JavaEE类加载机制

 2.1 java类加载器

   Java类加载器体系如下图所示:

 

  JavaEE ear包类加载器机制解析_第1张图片

 

2.2 JavaEE类加载器

  JavaEE中的类加载器是在上图的基础上实现的,如下图所示:

 

 JavaEE ear包类加载器机制解析_第2张图片

  从上图中可以看出,application server类加载器是ejb类加载器的父加载器,ejb包的类加载器是war包的父加载器。

(注:上图只是大体上的类加载器体系,不同的application server有不同的实现,但是具体的原理是一样的)。

三:实战

上面是关于类加载器的一些理论知识,下面通过一个具体的实例来验证以上理论。(以下实验均采用jboss 4.2 AS)

准备环境

首先在eclipse建立三个工程,如图:

 

 JavaEE ear包类加载器机制解析_第3张图片

 

   其中Demoejb工程包括两个文件,一个接口,一个实现类,Demoweb中包括一个DemoServlet类。其中我们编写一个测试类DemoUtil类。它里面包含一个静态变量,每次调用demo方法,都将count加一。具体的代码如下:

 

Demoear application.xml内容:

Java代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <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">  
  3.   <display-name>Demoear</display-name>  
  4.   <module id="myeclipse.1238995486296">  
  5.     <web>  
  6.       <web-uri>Demoweb.war</web-uri>  
  7.       <context-root>/Demoweb</context-root>  
  8.     </web>  
  9.   </module>  
  10.   <module id="myeclipse.1238994380625">  
  11.     <ejb>Demoejb.jar</ejb>  
  12.   </module>  
  13. </application>  

  

 

DemoUtil 类:

 

Java代码   收藏代码
  1. package com.yuquan.util;  
  2.   
  3. /** 
  4.  * @author yuquan 
  5.  * @createTime Apr 11, 2009 3:47:45 PM 
  6.  */  
  7. public class DemoUtil {  
  8.       
  9.     private static int count ;  
  10.   
  11.     public static int demo() {  
  12.           
  13.         return count++;  
  14.     }  
  15.   
  16. }  

 

Demoejb project 

DemoService代码:

Java代码   收藏代码
  1. package com.yuquan.service;  
  2.   
  3. import javax.ejb.Remote;  
  4.   
  5. /** 
  6.  * @author yuquan 
  7.  * @createTime Apr 6, 2009 1:08:41 PM 
  8.  */  
  9. @Remote  
  10. public interface DemoService {  
  11.       
  12.     public String execute();  
  13.       
  14.     public void print();  
  15.   
  16. }  

   

 

DemoServiceImpl代码:

Java代码   收藏代码
  1. package com.yuquan.service;  
  2. import javax.ejb.Remote;  
  3. import javax.ejb.Stateless;  
  4. import org.jboss.annotation.ejb.RemoteBinding;  
  5. import com.yuquan.util.DemoUtil;  
  6. /** 
  7.  * @author yuquan 
  8.  * @createTime Apr 6, 2009 1:10:24 PM 
  9.  */  
  10. @Stateless  
  11. @Remote(DemoService.class)  
  12. @RemoteBinding(jndiBinding="DemoServiceImpl")  
  13. public class DemoServiceImpl implements DemoService {  
  14.   
  15.     /* (non-Javadoc) 
  16.      * @see com.xmu.eartest.DemoService#execute() 
  17.      */  
  18.     @Override  
  19.     public String execute() {  
  20.           
  21.           
  22.         return String.valueOf(DemoUtil.demo());  
  23.   
  24.     }  
  25.   
  26.     @Override  
  27.     public void print() {  
  28.           
  29.         ClassLoader loader = DemoUtil.class.getClassLoader();  
  30.         while(loader !=null){  
  31.             System.out.println("Demoservice print :::"+loader);  
  32.             loader = loader.getParent();  
  33.         }  
  34.           
  35.           
  36.           
  37.     }  
  38.   
  39. }  

 

 

 

Demoweb project

DemoServlet代码:

 

Java代码   收藏代码
  1. package com.yuquan.action;  
  2. import java.io.IOException;  
  3. import java.io.PrintWriter;  
  4. import java.util.Arrays;  
  5. import java.util.Hashtable;  
  6. import java.util.List;  
  7. import javax.naming.Context;  
  8. import javax.naming.InitialContext;  
  9. import javax.naming.NamingException;  
  10. import javax.servlet.ServletException;  
  11. import javax.servlet.http.HttpServlet;  
  12. import javax.servlet.http.HttpServletRequest;  
  13. import javax.servlet.http.HttpServletResponse;  
  14. import com.yuquan.service.DemoService;  
  15. import com.yuquan.service.DemoService2;  
  16. import com.yuquan.util.DemoUtil;  
  17.   
  18. /** 
  19.  * @author yuquan 
  20.  * @createTime Apr 6, 2009 1:25:44 PM 
  21.  */  
  22. public class DemoServlet extends HttpServlet {  
  23.   
  24.     private Context cxt;  
  25.   
  26.       
  27.     @Override  
  28.     public void init() throws ServletException {  
  29.   
  30.         super.init();  
  31.         initContext();  
  32.     }  
  33.   
  34.     @Override  
  35.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
  36.             throws ServletException, IOException {  
  37.   
  38.   
  39.         DemoService service = (DemoService) getRemoteServieWithJndiName("DemoServiceImpl");  
  40.           
  41.           
  42.         System.out.println("service classloader is : "+service.getClass().getClassLoader());  
  43.         System.out.println("DemoService classloader is : "+DemoService.class.getClassLoader());  
  44.           
  45.         System.out.println("After the web being invoked, the static value is : " + DemoUtil.demo());  
  46.         System.out.println("After the ejb being invoked, the static value is : " + service.execute());  
  47.           
  48.         System.out.printf("Ejb print %s\n","---------------------");  
  49.         service.print();  
  50.         System.out.printf("web print %s\n","---------------------");  
  51.         this.print();  
  52.           
  53.         PrintWriter out = resp.getWriter();  
  54.         out.print("You have done the ear demo,pls see the console,and find the result!  ^_^");  
  55.     }  
  56.   
  57.     private Object getRemoteServieWithJndiName(String name) {  
  58.         Object o = null;  
  59.         try {  
  60.               
  61.             o = cxt.lookup(name);  
  62.         } catch (NamingException e) {  
  63.             // TODO Auto-generated catch block  
  64.             e.printStackTrace();  
  65.         }  
  66.           
  67.         return o;  
  68.     }  
  69.   
  70.     private void initContext() {  
  71.         Hashtable<String, String> environment = new Hashtable<String, String>();  
  72.   
  73.         environment.put(Context.INITIAL_CONTEXT_FACTORY,  
  74.                 "org.jnp.interfaces.NamingContextFactory");  
  75.         environment.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");  
  76.         environment.put(Context.PROVIDER_URL, "localhost:1099");  
  77.   
  78.         try {  
  79.             cxt = new InitialContext(environment);  
  80.   
  81.         } catch (NamingException e) {  
  82.   
  83.             e.printStackTrace();  
  84.         }  
  85.   
  86.     }  
  87.       
  88.     private void print(){  
  89.         ClassLoader loader = DemoUtil.class.getClassLoader();  
  90.         while(loader !=null){  
  91.             System.out.println("DemoServlet print ::: "+loader);  
  92.             loader = loader.getParent();  
  93.         }  
  94.     }  
  95.       
  96.       
  97.   
  98. }  

 

 

 

 

实验一

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 结果

Java代码   收藏代码
  1. 17:47:51,187 INFO  [STDOUT] service classloader is : WebappClassLoader  
  2.   delegate: false  
  3.   repositories:  
  4.     /WEB-INF/classes/  
  5. ----------> Parent Classloader:  
  6. java.net.FactoryURLClassLoader@d85409  
  7. 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}  
  8. 17:47:51,187 INFO  [STDOUT] After the web being invoked, the static value is : 0  
  9. 17:47:51,203 INFO  [STDOUT] After the ejb being invoked, the static value is : 1  
  10. 17:47:51,203 INFO  [STDOUT] Ejb print   
  11. 17:47:51,203 INFO  [STDOUT] ---------------------  
  12. 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}  
  13. 17:47:51,203 INFO  [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  14. 17:47:51,203 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f  
  15. 17:47:51,203 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0  
  16. 17:47:51,203 INFO  [STDOUT] web print   
  17. 17:47:51,203 INFO  [STDOUT] ---------------------  
  18. 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}  
  19. 17:47:51,203 INFO  [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  20. 17:47:51,203 INFO  [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f  
  21. 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 结果

Java代码   收藏代码
  1. 18:00:49,609 INFO  [STDOUT] service classloader is : WebappClassLoader  
  2.   delegate: false  
  3.   repositories:  
  4.     /WEB-INF/classes/  
  5. ----------> Parent Classloader:  
  6. java.net.FactoryURLClassLoader@7a8ba4  
  7. 18:00:49,609 INFO  [STDOUT] DemoService classloader is : WebappClassLoader  
  8.   delegate: false  
  9.   repositories:  
  10.     /WEB-INF/classes/  
  11. ----------> Parent Classloader:  
  12. java.net.FactoryURLClassLoader@7a8ba4  
  13. 18:00:49,609 INFO  [STDOUT] After the web being invoked, the static value is : 0  
  14. 18:00:49,625 INFO  [STDOUT] After the ejb being invoked, the static value is : 0  
  15. 18:00:49,625 INFO  [STDOUT] Ejb print   
  16. 18:00:49,625 INFO  [STDOUT] ---------------------  
  17. 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. 18:00:49,625 INFO  [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  19. 18:00:49,625 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f  
  20. 18:00:49,656 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0  
  21. 18:00:49,656 INFO  [STDOUT] web print   
  22. 18:00:49,656 INFO  [STDOUT] ---------------------  
  23. 18:00:49,656 INFO  [STDOUT] DemoServlet print ::: WebappClassLoader  
  24.   delegate: false  
  25.   repositories:  
  26.     /WEB-INF/classes/  
  27. ----------> Parent Classloader:  
  28. java.net.FactoryURLClassLoader@7a8ba4  
  29. 18:00:49,656 INFO  [STDOUT] DemoServlet print ::: java.net.FactoryURLClassLoader@7a8ba4  
  30. 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}  
  31. 18:00:49,656 INFO  [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  32. 18:00:49,656 INFO  [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f  
  33. 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.1 结构

实验三中,我们将Demoejb.jar包中的DemoUtil.类删除,将其打包到独立的util.jar包中,然后将util.jar包放到Demoweb.war包的lib目录下面,并且同时也需要把util.jar包放到Demoear.ear包的lib目录下。

4.2 结果

Java代码   收藏代码
  1. 18:07:51,343 INFO  [STDOUT] service classloader is : WebappClassLoader  
  2.   delegate: false  
  3.   repositories:  
  4.     /WEB-INF/classes/  
  5. ----------> Parent Classloader:  
  6. java.net.FactoryURLClassLoader@14322ba  
  7. 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}  
  8. 18:07:51,343 INFO  [STDOUT] After the web being invoked, the static value is : 0  
  9. 18:07:51,343 INFO  [STDOUT] After the ejb being invoked, the static value is : 0  
  10. 18:07:51,343 INFO  [STDOUT] Ejb print   
  11. 18:07:51,343 INFO  [STDOUT] ---------------------  
  12. 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}  
  13. 18:07:51,343 INFO  [STDOUT] Demoservice print :::org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  14. 18:07:51,343 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$AppClassLoader@18d107f  
  15. 18:07:51,343 INFO  [STDOUT] Demoservice print :::sun.misc.Launcher$ExtClassLoader@360be0  
  16. 18:07:51,343 INFO  [STDOUT] web print   
  17. 18:07:51,343 INFO  [STDOUT] ---------------------  
  18. 18:07:51,343 INFO  [STDOUT] DemoServlet print ::: WebappClassLoader  
  19.   delegate: false  
  20.   repositories:  
  21.     /WEB-INF/classes/  
  22. ----------> Parent Classloader:  
  23. java.net.FactoryURLClassLoader@14322ba  
  24. 18:07:51,343 INFO  [STDOUT] DemoServlet print ::: java.net.FactoryURLClassLoader@14322ba  
  25. 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}  
  26. 18:07:51,343 INFO  [STDOUT] DemoServlet print ::: org.jboss.system.server.NoAnnotationURLClassLoader@1632c2d  
  27. 18:07:51,343 INFO  [STDOUT] DemoServlet print ::: sun.misc.Launcher$AppClassLoader@18d107f  
  28. 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目录下,这样确保公用类是同一个类加载器加载。

你可能感兴趣的:(java,javaee,jboss,ClassLoader,ejb,File)