The NoteBook of EricKong

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            611 Posts :: 1 Stories :: 190 Comments :: 0 Trackbacks

          概述

          Java Classloader長期以來一直是導致混亂的根源,并且隨著 Java 語言的發展變得比以往任何時候都更為復雜。隨著 J2EE 應用服務器的出現,Java Classloader的復雜性進一步增加了?,F在,JVM 中的每個應用程序都可能有自己的Classloader層次,這將導致一個單獨的 JVM 可能包含許多的Classloader。而不同的J2EE應用服務器的Classloader的層次和策略也會有所差異,移植的復雜度也隨之上升。

          第一部分曾經提到:在移植的前期,我們應當分析系統的框架,澄清其中各個模塊之間的依賴關系(包括各個業務模塊之間的依賴關系,以及Web模塊和EJB模塊之間的依賴關系等),為了盡量避免模塊之間的交叉引用,我們甚至要重新打包。大家可能會問,我們的應用程序原本在WebLogic 、Tomcat、Jboss、Resin上能夠正常的運行,為什么我們要重新分析模塊之間的依賴關系,甚至重新打包呢?實際上,每個應用服務器都有著對于ClassLoader的不同實現。由于這些ClassLoader在裝載類文件時的行為會有所差異,所以如果應用程序未經重整和優化,很有可能在WebSphere應用服務器上運行失敗。然而,面臨這種差異,我們并不需要過多的恐慌。實際上,只要把握以下幾個原則,相信,交織在模塊依賴和類型裝載之中的問題,就可以迎刃而解了:

          一、 理解WebSphere ClassLoader的裝載層次和裝載策略

          二、 理清模塊間的依賴關系

          三、 將合適的裝載策略應用到各個模塊

          注意:我們并不需要了解WebLogic、Tomcat、JBoss、Resin等J2EE應用服務器的 Classloader的體系結構,只要我們理解了WebSphere的Classloader的體系結構,理清了應用程序的各個模塊之間的依賴關系,就可以解決由不同的J2EE應用服務器的Classloader的體系結構的差異給移植帶來的問題。

          首先我們簡單介紹一下WebSphere ClassLoader的體系結構,詳細內容可參看參考文檔。


          圖一、WebSphere Classloader層次結構圖
          圖一、WebSphere Classloader層次結構圖

          處于體系結構最上層的是系統 Classloader,它由bootstrap,extensions和 classpath三個classloader組成,bootstrap classloader會查找jre/lib下的類文件,從而加載核心類庫,extensions classloader使用系統屬性java.ext.dirs(通常是jre/lib/ext)查找和加載類文件,classpath classloader使用操作系統的classpath環境變量來查找和加載類文件。

          處于體系結構第二層的是WebSphere Extensions Classloader,它被用于裝載WebSphere的運行時類庫、J2EE類庫以及用戶代碼。它使用系統屬性ws.ext.dirs(通常是%was_root%\classes,%was_root%\lib和%was_root%\lib\ext)查找和加載類文件。

          處于體系結構第三層的是Application Classloader,用于加載EARs,RARs,JARs中的類文件。由于Application Classloader Policy是MULTIPLE,WebSphere應用服務器會為每一個EAR文件(EAR 1到EAR N)分配了一個Application Classloader。值得注意的是,如果WAR Classloader Policy如果設置為Application,那么Application Classloader可用于加載WARs中的類文件。

          處于體系結構最底層的是WAR Classloader,用于加載WAR文件中的類文件。圖中示意了WebSphere應用服務器為每一個WAR文件(WAR 1到 WAR N)分配了一個WAR Classloader的場景。值得注意的是,如果WAR Classloader Policy設置為MODULE,WebSphere應用服務器才會使用WAR Classloader加載WAR文件中的類文件。

            

          圖一中的單向箭頭表明了引用次序,即處于下面的Classloader可以調用其上層的類文件,而上層的Classloader無法調用其下面的類文件。這就為應用程序及其各個模塊的組織結構和設置Classloader策略提供了線索和限制。

          接下來,我們用一個實例來說明針對特定的模塊依賴關系應當如何設置WebSphere Classloader的裝載策略(policy)以及如何將各個模塊部署到合適的位置。

          有一個應用程序里有三個互相依賴的模塊,其調用關系如下:


          圖二、模塊關系圖
          圖二、模塊關系圖

          其中,WAR模塊中的SampleServlet類調用了UTILITY模塊中SampleUtility類,如代碼一所示:


          代碼一、SampleServlet.java
          public class SampleSevlet extends HttpServlet implements Servlet {
          	public void doGet(HttpServletRequest req, HttpServletResponse resp)
          		throws ServletException, IOException {
          		System.out.println("SampleServlet invokes SampleUtility");
          		new SampleUtility().callSampleSessionBean();
          	}
          	public void doPost(HttpServletRequest req, HttpServletResponse resp)
          		throws ServletException, IOException {
          	}
          }
          

          UTILITY模塊中SampleUtility類調用了EJB模塊中的SampleSessionBean類(通過callSampleSessionBean方法),如代碼二所示:


          代碼二、SampleUtility.java
          public class SampleUtility {
          public void callSampleSessionBean() {
          	 try{
          	     Context initCtx = new InitialContext();
          	     Object result = initCtx.lookup("ejb/sample/SampleSessionHome");
          	     SampleSessionHome ssh = (SampleSessionHome)
          PortableRemoteObject.narrow(
          					  result, SampleSessionHome.class);
          SampleSession ss = ssh.create();
          	     System.out.println("SampleUtility invokes SampleSessionBean");
          	     ss.callSampleUtility();
          	 }catch(Exception e){
          	     e.printStackTrace();
          	 }
          }
          public void finish() {
          	 System.out.println("The process has been finished");
          }
          }
          

          SampleSessionBean類又調用了UTILITY模塊中的SampleUtility類,如代碼三所示:


          代碼三、SampleSessionBean.java
          public class SampleSessionBean implements javax.ejb.SessionBean {
          	private javax.ejb.SessionContext mySessionCtx;
          	public javax.ejb.SessionContext getSessionContext() {
          		return mySessionCtx;
          	}
          	public void setSessionContext(javax.ejb.SessionContext ctx) {
          		mySessionCtx = ctx;
          	}
          	public void ejbCreate() throws javax.ejb.CreateException {
          	}
          	public void ejbActivate() {
          	}
          	public void ejbPassivate() {
          	}
          	public void ejbRemove() {
          	}
          	public void callSampleUtility() {
          		System.out.println("SampleSessionBean invokes SampleUtility");
          		new SampleUtility().finish();
          	}
          }
          

          這三個類之間的調用關系如下圖所示:


          圖三、順序圖

          下面我們通過不同的部署策略來深入探討WebSphere Classloader是如何影響應用程序運行的。1、如果將Utility模塊(JAR文件)拷貝到Web 模塊的 WEB-INF/lib 文件夾中,那么,Utility模塊將會被視為War模塊的一部分,如圖所示:


          圖四、Utility模塊(JAR文件)在Web 模塊中
          圖四、Utility模塊(JAR文件)在Web 模塊中

          如果我們將WAR Classloader Policy設置為MODULE(默認設置),如圖五所示:


          圖五、WAR Classloader Policy設置
          圖五、WAR Classloader Policy設置
          • 那么WebSphere應用服務器會使用WAR Classloader來裝載WAR模塊的類文件。而EJB模塊始終是通過Application Classloader進行裝載,由于Application Classloader處于WAR classloader的上層,EJB模塊無法引用War模塊的類文件。這樣,當SampleSessionBean調用SampleUtility時,會拋出異常:

            SystemOut     O SampleServlet invokes SampleUtility
            SystemOut     O SampleUtility invokes SampleSessionBean
            SystemOut     O SampleSessionBean invokes SampleUtility
            

            ExceptionUtil E CNTR0020E: 在 bean"BeanId(Sample#SampleEJB.jar#SampleSession, null)"上處理方法"callSampleUtility"時發生非應用程序異常。異常數據:

            java.lang.NoClassDefFoundError: sample/SampleUtility
            at sample.SampleSessionBean.callSampleUtility(SampleSessionBean.java:41)
          • 如果我們將WAR Classloader Policy設置為APPLICATION,那么WebSphere應用服務器會使用Application Classloader來裝載WAR模塊和EJB模塊的類文件。由于兩個模塊使用了相同的Classloader進行裝載,所以兩個模塊間的類可以相互引用應用服務器輸出的結果如下:

            SystemOut     O SampleServlet invokes SampleUtility
            SystemOut     O SampleUtility invokes SampleSessionBean
            SystemOut     O SampleSessionBean invokes SampleUtility
            SystemOut     O The process has been finished

          2、如果將Utility模塊拷貝到%was_root%\lib\ext , Websphere應用服務器會使用WebSphere Extensions Classloader加載Utility模塊中的類文件,由于WebSphere Extensions Classloader處于Application classloader和WAR classloader的上層,所以SampleServlet和SampleSessionBean可以引用到SampleUtility,但是SampleUtility引用不到SampleSessionBean,因此,當SampleUtility調用SampleSessionBean時會拋出異常。

          上面講的配置方法比較適合將已經開發完的J2EE應用程序按照原有的包結構部署到WebSphere上,但有的項目可能會在開發完部分子系統的時候,就要將J2EE應用程序遷移到WebSphere上,然后,在WSAD中繼續開發未完成的子系統。這樣,把UTILITY模塊打包到WAR模塊的WEB-INF/lib 文件夾中,將使得開發公用類變得繁瑣。幸運的是,WSAD提供了一個方式使公用類的開發和調試方法變得簡單、清晰。

          我們還用上面的實例進行演示。

          首先,我們要為UTILITY模塊創建一個JAVA項目。然后將utility.jar中的SampleUtility.java導入到此JAVA項目(頁可以稱作實用程序項目)當中。創建項目的結果如下圖所示:


          圖六、實用程序項目
          圖六、實用程序項目

          然后,將此實用程序項目添加到應用程序部署描述符當中,如圖七所示:

          1、 選擇"Sample"項目中的"應用程序部署描述符";

          2、 選擇"模塊";

          3、 在"項目實用程序JAR欄目中"點擊"添加";

          4、 選擇"Utility"項目,點擊"完成"。


          圖七、應用程序部署描述符
          圖七、應用程序部署描述符

          在所有依賴于此模塊的項目中,添加JAR模塊依賴項(主要包括EJB模塊和WEB模塊):

          1、在EJB模塊和WEB模塊分別編輯MANIFEST.MF文件,雙擊MANIFEST.MF文件可打開可視化編輯器編輯此文件


          圖八、MANIFEST.MF文件
          圖八、MANIFEST.MF文件

          2、 在EJB模塊的MANIFEST.MF文件中選擇Utility.jar


          圖九、EJB模塊的MANIFEST.MF文件
          圖九、EJB模塊的MANIFEST.MF文件

          3、 在WEB模塊的MANIFEST.MF文件中選擇Utility.jar


          圖十、WEB模塊的MANIFEST.MF文件
          圖十、WEB模塊的MANIFEST.MF文件

          接下來,我們可以啟動應用服務器,對SampleServlet進行測試,控制臺的顯示結果如下:

          SystemOut     O SampleServlet invokes SampleUtility
          SystemOut     O SampleUtility invokes SampleSessionBean
          SystemOut     O SampleSessionBean invokes SampleUtility
          SystemOut     O The process has been finished

          posted on 2011-12-27 16:06 Eric_jiang 閱讀(698) 評論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 云阳县| 大丰市| 阜平县| 永安市| 金阳县| 寻甸| 陆河县| 太和县| 定结县| 安平县| 分宜县| 阿克苏市| 井冈山市| 尉氏县| 商河县| 湘乡市| 冕宁县| 毕节市| 高碑店市| 塔城市| 沙雅县| 天峻县| 延长县| 大冶市| 莱西市| 洮南市| 五原县| 临夏县| 汨罗市| 奉贤区| 千阳县| 安化县| 哈巴河县| 潞西市| 登封市| 兴海县| 天台县| 海丰县| 兴义市| 调兵山市| 上犹县|