簡介
一般 J2EE 尤其是 WebSphere® Application Server 使用復雜的技術來 構建和裝入類。象許多開發者一樣,您可能想知道它們是如何組織在一起的,如何設計項目 才能節省時間並且充分利用開發環境。
本文提供有關 J2EE 規范以及如何使用它在 WebSphere Studio Application Developer 中構建項目的信息。除了創建基本的 J2EE 應用 程序,我們還將研究一些 Application Developer 的最佳實踐和高級功能部件。最後,我們 將為您提供一個扎實的基礎以便處理“可怕的”ClassNotFoundException。
J2EE 模塊
J2EE 規范描述三種類型的模塊:Web 模塊、EJB 模塊和應用程序 客戶機模塊。當部署到 J2EE 應用程序服務器時,通常會將所有這些模塊都壓縮到單個 J2EE 應用程序 EAR 文件中。在以下每一節中會討論一種模塊以及如何使用 Application Developer 來構建它。
Web 模塊
Web 模塊包含 HTML、圖像、JSps™、 Java™類和 servlet,以及創建 Web 應用程序所需的所有其它資源。象其它模塊一樣 ,Web 模塊包含一個部署描述符。在 Web 模塊中,部署描述符 web.xml 具有 servlet 初始 化和映射信息以及用於在應用程序服務器中運行 Web 模塊的其它設置。
Web 模塊有 兩個特殊的 Java 代碼文件夾: WEB-INF/classes 和 WEB-INF/lib 。classes 文件夾可以 包含“松散(loose)”Java 類(不在 JAR 文件中的類),並且可以將它用於 Web 應用程序范圍內的 servlet 或實用程序類。通常對於這個文件夾使用特殊的類裝入器, 因此如果對類進行更改,則應用程序服務器會自動將它們重新裝入。lib 文件夾可以包含也 是由 Web 應用程序使用的 JAR 文件(而不是 ZIp 文件!)。應該將第三方 JAR 文件和其 它實用程序 JAR 文件放入這個文件夾。如果 JAR 文件被其它 Web 或 EJB 模塊使用,則按 照下面的 企業應用程序一節中的說明將它們移動到“企業應用程序”項目中。
在 Application Developer 中,Web 模塊由 Web 項目表示,它包含兩個文件夾:source 和 webApplication。webApplication 文件夾包含擴展形式的完整 J2EE Web 模塊。source 文件夾用於存放 .java 文件,因為它們常常不是部署的 Web 模塊的一部分。當您在這個文 件夾中創建 Java 資源時,會自動編譯它們並將它們放入 webApplication/WEB-INF/classes 文件夾。這會使 Web 項目始終保持同步,並准備好測試或導出。
如果從 WAR 文件導入了 Web 模塊,則可能注意到 lib 文件夾中的 projectname_classes.jar 文件。這個文件包含導入的 WAR 文件的原始內容。如果 WAR 文 件包含源代碼,則刪除該文件,因為類將在 classes 文件夾中冗余地出現。
EJB 模塊
EJB 模塊包含 EJB bean、其特定於服務器的部署代碼、部署描述符和助手類(可選的) 。它們包含應用程序的業務邏輯,並且在一般情況下由 Web、Application Client 和其它 EJB 模塊調用。
在 Application Developer 中,EJB 模塊由 EJB 項目表示。這些項目還有兩個文件夾, bin 和 ejbModule。EJB 模塊的源代碼保存在 ejbModule 文件夾中。當更改和生成部署代碼 時,將這個文件夾中的 Java 類編譯到 bin 文件夾中。將剩余的資源(例如,部署描述符) 也復制到 bin 文件夾中。與 Web 項目的 webApplication 文件夾類似,bin 文件夾總是包 含完整的已部署的 EJB 模塊。與 Web 項目不同的是,不應該以任何手工方式修改 bin 文件 夾,否則可能丟失更改。在 ejbModule 文件夾中進行所有更改,將會自動編譯這些更改或將 它們復制到 bin 文件夾中。
如果從 EJB JAR 文件中導入 EJB bean,則可能注意到位於您項目根目錄中的 Xxx_importedClasses.jar 文件。這個文件包含導入的 EJB JAR 文件的原始內容。如果 JAR 包含源代碼,則刪除該文件,因為類將在 bin 文件夾中冗余地出現。
應用程序客戶機模塊
使用應用程序客戶機模塊以包含全功能客戶機 Java 應用程序(非基於 Web),它連接到 並使用在服務器中定義的 J2EE 資源。通過將客戶機代碼放入應用程序客戶機模塊而不是簡 單 JAR 文件,應用程序客戶機將得益於服務器的資源(它不需要將類路徑重新指定到 J2EE 和服務器 jar 文件)以及更方便的 JNDI 查詢(服務器填充初始上下文和其它參數)。
在 Application Developer 中,應用程序客戶機模塊由應用程序客戶機項目表示。對於 大部分模塊來說,可以象在 Java 項目中創建獨立的 Java 應用程序一樣工作。
企業應用程序(J2EE 應用程序)
企業應用程序是一個或多個 Web、EJB 或應用程序客戶機模塊的組合。作為這些其它模塊 的超集,它可以包含可能是多個模塊組合的完整的應用程序。除了是一個有效的組合機制外 ,企業應用程序還在完整的應用程序級別上部署和維護代碼,與作為單個代碼片段相比,這 更加容易。企業應用程序也可以重設被包含的模塊的部署描述符內部的設置,以更實用的方 式來組合或部署它們。
企業應用程序可能包含所含模塊使用的 JAR 文件。這允許在應用程序級別上共享代碼, 並且還是放置由多個 Web 或 EJB 模塊使用的實用程序 JAR 文件的最佳位置。通過將這些 JAR 文件放入企業應用程序而不是全局類路徑,它們同時也符合 J2EE 規范,在移動到新服 務器時不需要特殊發布和設置。
在 Application Developer 中,企業應用程序是由企業應用程序項目表示的。因為沒有 直接將源代碼構建到企業應用程序,所以這些項目沒有子文件夾。
WebSphere 類裝入器
WebSphere Application Server 使用幾個類裝入器來遵循 J2EE 規范。除了使用類路徑 環境變量定位並裝入類的常規類裝入器之外,還有許多正在工作的其它類裝入器。
下面的圖 1 顯示了 WebSphere 中正在工作的類裝入器的簡化圖。每個橢圓代表一個類裝 入器,方括號中的文本描述了類裝入器在何處查找類。
圖 1. WebSphere 類裝入器
在頂部,常規 Java 系統類裝入器使用類路徑環境變量來裝入類。第二個類裝入器是特定 於 WebSphere 的並且使用 ws.ext.dirs 環境變量來裝入類。(有關如何更改這個環境變量 的信息,請參閱“發行版說明”。在刷新中,您將能在服務器實例編輯器中更改這個變量。 )除了裝入所有用戶代碼之外,這個裝入器還裝入在運行時所需的所有 WebSphere 和 J2EE 類。最後,由一個或多個模塊類裝入器來裝入運行在服務器中的模塊。它們遵循前面討論的 J2EE 類裝入規則來從應用程序裝入類和 JAR 文件。
上面的圖 1 中最重要的是將每個類裝入器定義成其上面的類裝入器的子類。無論何時需 要裝入類,類裝入器通常將請求委派給其父類裝入器。如果父類裝入器無法找到該類,則原 始類裝入器試圖裝入該類。請求只能在樹中上行而不能下行。如果請求 WebSphere 類裝入器 查找 J2EE 模塊中的類,它不能下行至模塊類裝入器來查找該類,並且將出現 ClassNotFoundException。一旦由類裝入器裝入一個類,則它嘗試要裝入的任何新類將重用 同一個類裝入器,或者沿著該路徑上行直到找到該類為止。
兩種情況下,您可能遇到問題。
WebSphere、J2EE 和任何全局類無法“看見”包含在應用程序中的類。如果將 JAR 文件 添加到全局類路徑或 ws.ext.dirs 屬性中,則它就不再取決於模塊中的類了。
如果需要全局地將數據庫驅動程序或實用程序 JAR 添加到類路徑,則必須將它們添加到 ws.ext.dirs 屬性而不是添加到全局類路徑。如果錯誤地將它們添加到全局類路徑,則它們 將無法裝入(例如)J2EE JAR 文件中的連接池類。這將再次作為 ClassNotFoundException 出現在數據庫驅動程序上,但是,對數據庫驅動程序類而言,向下查看 J2EE 類是錯誤的。
查找資源(屬性文件、圖像等)的公共可移植技術是使用類裝入器的 findResourceXX 方 法。基於前面的討論,您可以看出為什麼對這個作業使用正確的類裝入器是重要的。例如, 如果使用 WebSphere 類裝入器,您將不能在模塊中找到任何資源。
WebSphere 類裝入器隔離方式
正如前面提到的,WebSphere 使用幾個類裝入器來從部署到服務器的模塊中裝入應用程序 代碼。這些類裝入器的數量和功能取決於已在 WebSphere 服務器配置中指定的類裝入器隔離 方式(也稱為模塊可見性方式)。有四種設置可供選擇:
Server? 對於整個服務器都使用同一個類裝入器。
Application? 對於每個企業應用程序使用獨立的類裝入器。
Module? 對每個模塊使用單獨的類裝入器。
Compatibility-- 使用與 WebSphere Application Server 3.0.x 和 3.5.x 相同的類裝 入器模式。這允許代碼具有跨越企業應用程序邊界的可見性。不贊成使用該方式,除非您正 在從舊的服務器中遷移代碼。
構建更大的 ? 模塊相互依賴性
當在企業應用程序內構建跨越多個模塊的應用程序時,請確保一個模塊中的類可以看到另 一個模塊中它所使用的類。對於想使用企業應用程序之外的 JAR 文件的模塊來說也是如此, 並且對於編譯時和運行時都必須完成該操作。
在編譯時,更新類路徑是很簡單的。您只需要編輯項目的屬性然後更改 Java 構建路徑以 包含其它項目或 JAR 文件,但且慢動手!我們將介紹一種更簡便的方法,它只用一步就在編 譯時和運行時更新它。常規情況下,不要手工更改 J2EE 項目的 Java 構建路徑。下列步驟 將自動更新並維護構建路徑,然後與運行時保持同步。
在運行時,當引用其它模塊時,應用程序必須遵循 J2EE 規范。如上面的“WebSphere 類 裝入器”所述,不要簡單地將一個項目放在其中一個全局類路徑上,因為這可能會對類裝入 產生嚴重影響,並且使所有其它應用程序都能看見您的代碼,後果同樣嚴重。
您也不應該依賴於 WebSphere 類裝入器隔離方式。如果您的服務器以(例如) Application 方式運行,則可能不必做任何更改就能使應用程序在運行時工作。不要依賴於 它!以後,可能需要將應用程序部署到在 module 方式下運行的服務器中,並且您將發現應 用程序不再工作。無論使用哪種隔離方式,請遵循下面建議的步驟以確保您的應用程序正確 。
解決方案將利用 J2EE 模塊是 JAR 文件這一事實。所有 JAR 文件可能具有一個 META- INF 文件夾,它包含 MANIFEST.MF 文件,該文件可能包含引用其它 JAR 文件的類路徑。通 過將一個其它模塊作為一個項加入清單的類路徑,當前模塊可以利用其包含的類。還可以從 企業應用程序中將 JAR 文件添加到清單。對這個簡單解決方案的唯一限制是,其它模塊決不 能依賴於 Web 模塊中的類,因為 Web 模塊不是用位於其根處的類構建的。換句話說,模塊 不可能使用清單以依賴 Web 模塊中的類。
Application Developer 提供了更新清單文件的簡單方法。在 Navigator 或 J2EE 視圖 中的 J2EE 項目上單擊右鍵,然後選擇 Edit Module Dependencies。這會產生一個對話框( 參閱下面的圖 2),它顯示了一個復選框列表,其中包含這個項目可依賴的所有其它模塊和 JAR 文件。通過選擇列表中的模塊或 JAR 文件,可以同時更新清單和構建路徑。這確保清單 和構建路徑總是同步的,並且提供了更改您項目相互依賴性的更簡便方法。
圖 2. 編輯模塊相互依賴性對話框
應該將 JAR 文件放在哪裡?
如果僅在單個 Web 應用程序中使用 JAR 文件,則應該總是將 JAR 文件放在 Web 項目的 webApplication/WEB-INF/lib 文件夾中。這個文件夾中的 JAR 文件將自動添加到 Java 構 建路徑,並且當移動到另一個服務器時,將不需要任何進一步的設置。
如果 JAR 文件由同一個應用程序中的多個模塊使用,則將 JAR 文件放入企業應用程序中 。您將需要使用 Edit Module Dependencies 功能部件來設置清單文件和 Java 構建類路徑 。
如果仍然想將 JAR 文件放置在全局類路徑上,(我們強烈建議您不要使用這個方法)您 必須確定它是否應該放在全局類路徑或 ws.ext.dirs 上。這個判定很簡單。如果 JAR 文件 需要訪問任何 J2EE 或 WebSphere 類或者已經添加到 ws.ext.dirs 中的任何其它 JAR,還 必須將它放置在 ws.ext.dirs 屬性上。否則,您可以自由使用這兩種屬性。
最後要記住的一件事是 JAR 文件在路徑中的位置越高,其包含的內容就越少。如果與 JAR 文件有牢固的相互依賴性,則更新使用該 JAR 文件的每個項目的 Java 構建路徑。將它 添加到全局類路徑或 ws.ext.dirs 屬性還意味著,必須單獨地從應用程序發布 JAR 文件, 並且當移動到另一個服務器時,必須再次設置服務器類路徑。
使用一個全局類路徑的唯一好處是次要的 ? 您可以在服務器上節省少量的磁盤空間。重 大的缺點是您的應用程序現在很脆弱,其他人可能更改您所依賴的類。例如,假設您依賴某 些第三方記錄類,並且因為幾乎所有應用程序都使用這些記錄類,所以您決定在全局類路徑 上部署它們。使用記錄類的版本 1 來測試應用程序。六個月後,部署了另一個應用程序,它 需要記錄類的版本 2,因此更新了日志記錄 JAR。現在,您的應用程序運行在一個從來沒有 測試過的環境中。
類還是 JAR?
您需要作出的另一個決定是使用松散類還是使用 JAR。使用松散類的一個很大的好處是容 易調試和部署它們。使用松散類,可以通過按 Ctrl-S 進行更改,如果它在可重裝入的類路 徑上(象 WEB-INF/classes ),則可以立即使用它。如果它不在一個可重裝入的類路徑上, 並且如果您正在使用 WebSphere Application Server,則只需要重新啟動項目,然後就可使 更改生效了。要完成這個任務,在 Navigator 中的項目上單擊右鍵,然後從彈出菜單中選擇 Restart project。
如果您正在使用 JAR 文件,則還需要完成收集所有類和重構建 JAR 文件的其它構建步驟 。您的更改不能很快生效。使用 JAR 文件的好處是使部署更清晰一點,並且更接近期望的生 產環境。
如果處於項目的早期階段,此時經常更改助手類,則您會發現使用松散類將更方便。如果 類僅由單個 Web 模塊使用,則將類放入 WEB-INF/classes 文件夾。為了使用類,總是使用 包,因為當 Java 規范處理缺省包中的類時是不精確的。
如果類由 EJB 使用,則讓您的一個 EJB 項目保留助手類(將它們放在 ejbModule 文件 夾下),讓需要這些助手類的其它 EJB 模塊依賴於該 EJB 項目。將您的所有助手類都收集 到單個 EJB 項目中,因為在項目之間不建立循環相互依賴性是 非常重要的。好的項目結構 規定相互依賴性必須始終采用樹的形式。一旦助手類成熟並且沒有太多更改時,就切換至 JAR 方法。
如果實用程序類由企業應用程序和常規 Java 應用程序共同使用將會怎樣?最簡單的方法 是使用這些類的 JAR。您可以用一個更復雜的替代方法:
創建一個簡單的 Java 項目以存儲實用程序類。
手工更新依賴於這個項目的項目 Java 構建路徑。
手工更新服務器實例以包含簡單 Java 項目的 bin 文件夾。
開發時類路徑對比運行時類路徑
在編譯時,Java 編譯器需要知道您的代碼引用的每個類或 JAR 文件,這樣就可以安全地 對這些類進行編譯和類型檢查。Java 編譯器使用項目級別屬性 Java Build path 作為這個 信息的唯一源。其它操作(如 Edit Module Dependencies)會操縱構建路徑,但是構建路徑 是權威性源。
在運行時,應用程序服務器使用完全不同的機制來查找和裝入類。運行時不知道任何 Java 構建路徑,因此您的應用程序可能正確編譯,但是在運行時仍然有 ClassNotFoundException。我們希望本文可以揭開這個過程的一些秘密。