主題:JavaEE ear包類加載器機制解析

轉載自:http://www.iteye.com/topic/366112  可能有不適合現在技術的地方,不過原理沒變,我就是根據這個解決了ear部署ejb3.0的應用時候,找不到Session Bean 的問題。

 

在介紹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代碼:

 

package com.yuquan.action;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.yuquan.service.DemoService;
import com.yuquan.service.DemoService2;
import com.yuquan.util.DemoUtil;

/**
 * @author yuquan
 * @createTime Apr 6, 2009 1:25:44 PM
 */
public class DemoServlet extends HttpServlet {

	private Context cxt;

	
	@Override
	public void init() throws ServletException {

		super.init();
		initContext();
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {


		DemoService service = (DemoService) getRemoteServieWithJndiName("DemoServiceImpl");
		
		
		System.out.println("service classloader is : "+service.getClass().getClassLoader());
		System.out.println("DemoService classloader is : "+DemoService.class.getClassLoader());
		
		System.out.println("After the web being invoked, the static value is : " + DemoUtil.demo());
		System.out.println("After the ejb being invoked, the static value is : " + service.execute());
		
		System.out.printf("Ejb print %s\n","---------------------");
		service.print();
		System.out.printf("web print %s\n","---------------------");
		this.print();
		
		PrintWriter out = resp.getWriter();
		out.print("You have done the ear demo,pls see the console,and find the result!  ^_^");
	}

	private Object getRemoteServieWithJndiName(String name) {
		Object o = null;
		try {
			
			o = cxt.lookup(name);
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return o;
	}

	private void initContext() {
		Hashtable<String, String> environment = new Hashtable<String, String>();

		environment.put(Context.INITIAL_CONTEXT_FACTORY,
				"org.jnp.interfaces.NamingContextFactory");
		environment.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");
		environment.put(Context.PROVIDER_URL, "localhost:1099");

		try {
			cxt = new InitialContext(environment);

		} catch (NamingException e) {

			e.printStackTrace();
		}

	}
	
	private void print(){
		ClassLoader loader = DemoUtil.class.getClassLoader();
		while(loader !=null){
			System.out.println("DemoServlet print ::: "+loader);
			loader = loader.getParent();
		}
	}
	
	

}

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目錄下,這樣確保公用類是同一個類加載器加載。


 

發佈了28 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章