根據Java EE規范的要求,理想情況下,應用程序服務器應為其部署的應用程序提供使用任何實用程序庫和任何版本的自由,而不必考慮要使用同一庫的并發應用程序的存在。
這也稱為名稱空間隔離(Java EE 5規范,EE.8.4節)。 但是,從不同名稱空間加載類可能會引起一些不易解決的問題:例如,如果我在應用程序中打包了新版本的實用程序庫,而應用程序加載了同一個庫的舊版本,將會發生什么情況?服務器? 或者,如何在應用程序服務器的同一實例中同時使用同一實用程序庫的兩個不同版本? 多年來,JBoss AS類加載策略已經發生了明智的變化:基本上,應用服務器的4.x版本使用UnifiedClassLoader ,其目的是減少運行的應用程序之間的通信開銷,因為類數據可以通過引用或簡單副本共享。

使用UnifiedClassLoader無法解決的主要未解決問題之一是類加載依賴項。 這樣的想法是,如果一個應用程序(A)使用另一個應用程序(B)的類,則系統應該知道在重新部署B時重新部署A,否則它將引用陳舊的類。 實際上,有兩種嘗試來使這項工作有效,而無需用戶進行任何配置。 兩種嘗試均未真正奏效,并且均被放棄。 自從JBoss AS 5.0引入以來,基于新的虛擬文件系統(VFS)的新類加載器。 實施VFS是為了簡化和統一應用程序服務器中的文件處理。 新的類加載器名為VFS類加載器,它使用VFS查找JAR和類文件。 即使這表示在JBoss AS 5.0中如何加載類,也發生了重大變化,但是所產生的行為與JBoss AS以前的版本非常相似。
錯誤的常見來源是在部署中包括由容器提供的API類。 這會導致創建該類的多個版本,并且部署無法正確部署。 AS7中的類加載標志著與以前的嘗試大相??徑庭。 現在,類加載基于JBoss模塊項目,并且所部署的任何應用程序實際上都是模塊。 肯定的讀者會提出一些疑問:分配給已部署應用程序的模塊名稱是什么? 而且,AS如何處理模塊之間的依賴關系? 讓我們在單獨的部分中回答每個問題:
了解模塊名稱
了解模塊名稱不是學術活動。 實際上,我們可以在模塊之間建立依賴關系,因此在許多情況下,需要知道分配給應用程序的模塊名稱是哪一個。 因此,打包為頂級歸檔文件(例如WAR,JAR和SAR)的應用程序將獲得以下模塊名稱:
deployment.[archive name]
例如,將部署一個名為WebExample1.war的Web應用程序作為模塊名稱:
deployment.WebExample1.war
另一方面,在包含嵌套模塊的應用程序(例如EAR歸檔文件)上,將使用以下分類為每個單個歸檔文件分配模塊名稱:
deployment.[ear archive name].[sub deployment archive name]
因此,如果將相同的Web應用程序包含在存檔EnterpriseApp.ear中 ,則將使用以下名稱進行部署:
deployment.EnterpriseApp.ear.WebExample1.war
查找隔離級別
在第2章“配置應用程序服務”中 ,我們有意部署了使用log4j Api的應用程序,并將log4j庫添加到WEB-INF / lib文件夾中。 該應用程序的部署很順利,省去了一個問題:為什么我們需要添加已經作為模塊包含在應用程序服務器中的庫? (在我們的例子中,在modules \ org \ apache \ log4j \ main路徑中)。
一般規則是,在JBoss AS 7上,每個已部署的應用程序模塊都與其他模塊隔離開來。 這意味著,默認情況下,它在AS模塊上不可見,在AS模塊上也不對應用程序可見。
但是,使用應用程序服務器模塊非常容易,并且可以用一句話來概括:將依賴項添加到所需模塊中,AS就會使用它。 應用程序服務器會自動添加依賴項,或者它們需要由用戶發出信號:
- 核心模塊庫(即企業類)被視為隱式依賴項,因此當部署者檢測到它們的使用時,它們會自動添加到您的應用程序中
- 用戶需要在應用程序的MANIFEST文件或定制的JBoss部署文件jboss-deployment-structure.xm l中明確聲明其他模塊庫(有關更多信息,請參見“高級部署策略”部分)。
隱式依賴:
指出企業應用程序通常使用的Api依賴項可能很麻煩。 因此,正如我們預期的那樣,應用程序服務器會自動為您添加它們。 當應用服務器檢測到某些該模塊的典型注釋或配置文件時,將添加一些注釋或配置文件。 例如,添加beans.xml文件會觸發自動焊接依賴。
以下思維導圖顯示了隱式添加到應用程序中的模塊的綜合視圖:

該圖像的含義很簡單:如果您的應用程序使用了所示的任何核心模塊,那么您無需指定任何依賴項,因為應用程序服務器能夠自動鏈接該模塊。 顯式依賴項不能被定義為隱式依賴項的模塊需要由用戶聲明。 在最初的示例中,未將log4j庫作為隱式依賴項提及,因此我們不得不將log4j JAR與應用程序打包在一起。 但是,我們可以指示部署者使用log4j庫,該庫捆綁在應用程序服務器分發中。 實現它的最簡單且推薦的方法是在META-INF / MANIFEST.MF中包括Dependencies:[module]聲明。 在我們的情況下,要使您的應用程序依賴于log4j,只需在清單文件中包含以下代碼:
依賴項:org.apache.log4j
請注意,模塊名稱并不總是與庫的軟件包名稱匹配。 實際的模塊名稱由module元素的name屬性在module.xml文件中指定。
用戶通常將使用Eclipse(或任何其他IDE)來更新清單文件,而無需執行任何繁瑣的存檔更新:

您不僅限于單個依賴項,還可以添加多個以逗號分隔的依賴項。 例如,為了同時添加對log4j和Apache Velocity Api的依賴,可以使用以下命令:
依賴項:org.apache.log4j,org.apache.velocity
您甚至可以通過添加export關鍵字將一個應用程序模塊使用的依賴項導出到其他應用程序。 例如,在較早的應用程序中,我們現在將依賴項導出到其他模塊:

標為依賴于Deployment.WebApp.war模塊的應用程序還將有權訪問其依賴項:

export參數還可以用于將依賴項導出到耳朵中包含的所有子部署。 因此,如果從耳朵的頂層(或通過ear / lib目錄中的jar)導出依賴關系,則該依賴關系也將對所有子部署單元可用。
在META-INF / MANIFEST.MF中,您還可以指定其他命令,這些命令可以修改服務器部署程序的行為。 例如,可以添加可選屬性,以指定如果在部署時未找到模塊,則部署不會失敗。
最后,當指定了services關鍵字時,部署者將嘗試加載放置在歸檔文件的META-INF / services中的服務。
服務Api已在Java SE 6中公開。服務可以定義為一組編程接口和類,它們提供對某些特定應用程序功能或特性的訪問。 服務提供者接口(SPI)是服務定義的一組公共接口和抽象類。
您可以通過實現服務提供商API來定義服務提供商。 通常,您將創建一個JAR文件來保存您的提供程序。 要注冊您的提供程序,您必須在JAR文件的META-INF / services目錄中創建一個提供程序配置文件。 將services屬性添加到META-INF / MANIFEST.MF時,您實際上將能夠加載META-INF / services目錄中包含的服務。
有關SPI Api的出色介紹,請訪問:http://java.sun.com/developer/technicalArticles/javase/extensible。
設置全局模塊
該選項與用于加載公共庫的舊AS方法有點類似,您曾經將它們放置在文件夾JBOSS_HOME / common / lib中。 如果在standalone.xml / domain.xml中定義了名為global-modules的部分,則將使該模塊可被其他AS模塊訪問。 例如,您可以選擇使用以下部分來代替聲明對log4j的依賴關系:
<subsystem xmlns="urn:jboss:domain:ee:1.0"><global-modules><module name="org.apache.log4j" /></global-modules>
</subsystem>
盡管通常不建議使用這種方法,但是由于它使我們回到了單片應用服務器的概念,因此它仍然可以帶來一些好處。 例如,如果您要遷移一些較舊的應用程序,并且您不希望或者根本無法將依賴關系指定到存檔中。
進階部署策略
到目前為止,我們所學的知識足以配置許多類型的應用程序。 如果您使用的是復雜的歸檔配置,例如具有多個模塊和依賴項的EAR歸檔,那么在單個文件中定義類加載策略將很有用。
配置文件jboss-deployment-structure.xml可以做到這一點。 使用此文件的優點有很多:
- 您可以在一個文件中定義所有應用程序模塊的依賴關系
- 您可以通過包含/排除所有或部分模塊來使用更細粒度的方式加載模塊類
- 您可以為打包在企業歸檔中的應用程序定義類加載隔離策略
讓我們看一些實際的例子,jboss-deployment-structure.xml可以為您做什么。
設置單個模塊依賴性
我們已經學習了如何使用歸檔文件的MANIFEST文件中的Dependencies屬性來激活log4j依賴關系。 使用jboss-deployment-structure.xml文件可以達到相同的效果。 讓我們回顧一下存檔結構,它基本上是由一個名為WebApp.war的Web應用程序組成的。
如您所見,文件jboss-deployment-structure.xml需要放置在EAR的META-INF文件夾中。

其內容如下:
<jboss-deployment-structure><sub-deployment name="WebApp.war"><dependencies><module name="org.apache.log4j" /></dependencies></sub-deployment>
</jboss-deployment-structure>
文件jboss-deployment-structure并非專用于EAR。 實際上,您也可以將其放置在WebApp應用程序中,方法是將其放置在存檔的WEB-INF文件夾中。 但是,它僅適用于頂級歸檔。 因此,如果將jboss-deployment-structure.xml放在WAR的WEB-INF文件夾中,并且將WAR打包在EAR歸檔中,則將忽略jboss-deployment-structure.xml。
該文件的相關部分是sub-deployment元素,該元素引用Web應用程序(包括其中的依賴項)。 預期的結果是應用程序服務器將觸發對log4j Api的依賴關系,因此我們的Web應用程序將看到該依賴關系。
排除服務器自動依賴項
在本章的前面,我們已經展示了當滿足某些條件時,應用程序服務器如何能夠自動觸發某些依賴關系。 例如,如果部署JSF應用程序(包含faces-config.xml文件),那么將自動添加JSF 2.1 Api實現。
例如,這可能并不總是所需的選項,因為您要為該模塊提供另一個發行版實現。 您可以使用jboss-deployment-structure.xml中的排除部分輕松實現此目標,如下所示:
<jboss-deployment-structure><deployment><exclusions><module name="javax.faces.api" /><module name="com.sun.jsf-impl" /></exclusions> <dependencies><module name="javax.faces.api" slot="1.2"/> <module name="com.sun.jsf-impl" slot="1.2"/></dependencies> </deployment>
</jboss-deployment-structure>
注意,在“依賴項”部分中,我們添加了備用JSF 1.2實現,您的應用程序將使用該實現。 實際上,此JSF實現隨slot屬性指定的文件夾下的應用程序服務器分發以及javax.faces.api模塊路徑一起提供。 在我們的例子中,這對應于JBOSS_HOME / modules / javax / faces / api / 1.2文件夾。
隔離子部署
假設您有一個由Web應用程序,EJB模塊和包含實用程序類的JAR文件組成的應用程序。 所有子部署均位于存檔的根目錄,因此它們將能夠彼此看到。 但是,假設您的Web應用程序自身包含相同EJB的某些實現,這可能會很方便。 這是絕對可能的,因為Java EE 6規范允許您的Web應用程序在WEB-INF / classes或WEB-INF / lib文件夾中包括EJB類。

類加載器如何解決此沖突? 加載用于避免已加載類之間的任何沖突的類時,應用程序服務器類加載器具有優先級列表。
- 容器(包括Java EE API)自動將模塊的最大優先級賦予模塊。 包含在modules文件夾中的庫在此類別中。
- 然后,用戶在打包檔案的MANIFEST.MF中將其指示為Dependencies(或在jboss-deployment-structure.xml文件中)。
- 接下來,打包在應用程序內部的庫,例如WEB-INF / lib或WEB-INF / classs中包含的類。
- 最后,打包在同一EAR歸檔文件中的庫(在EAR的lib文件夾中)。
因此,在此示例中,位于WEB-INF文件夾中的EJB庫將“隱藏” EJB.jar頂級部署的實現。 無論這是否是容器中所需的操作,您仍然可以覆蓋它:
<jboss-deployment-structure> <ear-subdeployments-isolated>false</ear-subdeployments-isolated> <sub-deployment name="WebApp.war"><dependencies><module name="deployment.App.ear.EJB.jar" /> </dependencies></sub-deployment>
</jboss-deployment-structure>
在此示例中,我們向EJB.jar添加了一個依賴關系,該依賴關系位于存檔的根目錄,它將覆蓋Web應用程序中打包的實現。
請注意, ear-subdeployments-isolated元素位于文件頂部。 通過設置EAR隔離級別,您將能夠指示子部署模塊是否彼此可見。
此屬性的默認值為false ,這意味著子部署模塊將能夠看到彼此。 如果將隔離設置為true,則每個模塊將由不同的類加載器接收,因此,在我們的示例中,Web應用程序將無法找到EJB.jar和Utility.jar庫中包含的類。
如果要使部署保持隔離狀態,但允許其中一些可見,則可以選擇以下幾種方法:
- 將庫移至EAR / lib文件夾,以便將其作為單獨的模塊使用
- 使用依賴關系或調用應用程序的MANIFEST.MF文件中的類路徑指定依賴關系
從以下屏幕截圖中,您可以看到如何通過將公共庫放置在lib文件夾中并向EJB類添加依賴項來正確設置EAR:

這是jboss-deployment-structure.xm l中所需的相應配置:
<jboss-deployment-structure> <ear-subdeployments-isolated>true</ear-subdeployments-isolated> <sub-deployment name="WebApp.war"><dependencies><module name="deployment.App.ear.EJB.jar" /> </dependencies></sub-deployment>
</jboss-deployment-structure>
可以使用共享庫中的打包庫,因為Java EE 5通常用于保存EAR的所有模塊所使用的JAR文件。
共享庫的默認名稱是lib,但是您可以隨時使用META-INF / application.xml文件中的library-directory元素覆蓋它。 例如,假設您要使用公用文件夾來保存共享庫,則可以將其添加到application.xml中 :
<library-directory>common</library-directory>
作為附帶說明,我們建議您避免在共享文件夾中放置聲明組件的注釋(例如EJB3),因為它可能對部署過程產生意想不到的不良后果。 因此,我們強烈建議您僅將實用程序類放在共享庫文件夾中。
使用類路徑聲明解決依賴關系:
到目前為止,我們已經使用JBoss的方法解決了模塊之間的依賴關系,我們顯然建議將其作為首選。 盡管如此,我們也應該考慮Java引用EAR文件中包含的一個或多個庫的可移植方式。
可以使用模塊的MANIFEST.MF文件中的Class-Path屬性來完成此操作,該屬性需要引用另一個應用程序無法看到的庫(請看前面的示例,帶有隔離集的部署單元)為true )。
例如,假設您需要從Web應用程序中引用Utility.jar應用程序,然后只需將以下內容添加到META-INF / MANIFEST.MF中:
- 清單版本:1.0
- 類路徑:Utility.jar
實際上,您可以在Class-Path中包含多個庫,并以逗號分隔它們,這與使用JBoss的Dependencies屬性的方式非常相似。 與Dependencies屬性不同,Class-Path屬性指向引用依賴庫的實際JAR文件名(而不是模塊名)。
在類路徑方法和JBoss的Dependencies方法之間進行選擇,取決于應用程序的結構:通過使用JBoss的Dependencies,您可以購買到更多的選項,尤其是將Dependencies導出到其他部署的能力。較早。 支持JBoss的Dependencies方法的另一點是引用模塊的能力,這些模塊實際上并未打包在應用程序中。 例如,我們已經了解了如何向服務器分發的一部分log4j Api添加依賴項。
另一方面,類路徑方法的主要優點取決于應用程序的可移植性。 因此,如果您將全便攜式解決方案作為優先事項,則可以考慮切換到Class-Path清單屬性。

這是示例章節,摘自Francesco Marchioni編輯的JBoss AS 7 Configuration Deployment and Administration一書,該書正在運行一個名為mastertheboss.com的JBoss門戶。
翻譯自: https://www.javacodegeeks.com/2012/09/jboss-as-7-classloading-explained.html