領先一步
VMware 提供培訓和認證,以加速您的進展。
了解更多上個月我在土耳其進行了 Core Spring 訓練。在課程結束時,我討論了一個應用程式的架構,一些參與者將在完成課程後建構該應用程式。這個應用程式將包含一個 ear 檔案,其中包含多個 war 檔案,問題是是否有可能定義一個單一的 ApplicationContext,它可以作為所有 war 檔案的 WebApplicationContexts 的共用父層。此上下文將保存服務、DAO 和其他非特定於單一 Web 模組的 Bean 定義。
實際上,Spring 使這件事變得非常容易,但課程和參考手冊都沒有詳細解釋如何從您的 Web 應用程式中使用此功能。因此,我編寫了一個簡短的範例應用程式來說明其運作方式,我將在我的第一篇部落格文章中在此處討論。
在典型的 Spring Web 應用程式中,您使用 ContextLoaderListener(或者,如果您使用的是 Servlet 2.2 / 2.3 容器,則使用 ContextLoaderServlet)來引導 WebApplicationContext。您透過 web.xml 中的上下文參數來配置此類別使用的 ContextLoader。如果您使用過此功能,您可能對以下內容很熟悉contextConfigLocation參數,它讓您可以指定哪些檔案構成要建構的 WebApplicationContext。
事實證明,還有另一個參數可用於以宣告方式取得所需的功能:parentContextKey。使用此參數,您可以指示 ContextLoader 使用另一個名為 ContextSingletonBeanFactoryLocator 的類別來搜尋名為parentContextKey的 Bean,該 Bean 定義在名稱符合特定模式的組態檔中。預設情況下,此模式為 'classpath*:beanRefContext.xml',表示類別路徑上所有名為 beanRefContext 的檔案。(對於普通的 SingletonBeanFactoryLocator,它是 'classpath*:beanRefFactory.xml')此 Bean 必須是 ApplicationContext 本身,並且此上下文將成為 ContextLoader 建立的 WebApplicationContext 的父上下文。但是,如果此上下文已存在,則將使用該上下文,並且不會建立新的上下文(因此稱為 SingletonBeanFactoryLocator)。
讓我們看看這意味著什麼:首先,我們需要在我們的 ear 中放入一個單獨的 jar,其中包含服務、DAO 等的程式碼。在此 jar 內部,我們放置一個 beanRefContext.xml 檔案,其中包含 ApplicationContext 的單一 Bean 定義。通常,這將是一個 ClassPathXmlApplicationContext。然後,該 Bean 定義將引用一個或多個「常規」Bean 組態檔,這些檔案包含服務 Bean 和 war 檔案中的程式碼要使用的其他內容;類似於這樣
<!-- contents of beanRefContext.xml:
notice that the bean id is the value specified by the parentContextKey param -->
<bean id="ear.context" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>services-context.xml</value>
</list>
</constructor-arg>
</bean>
最後,我們需要使用Class-Path在每個 war 的 MANIFEST.MF 檔案中的條目,使此 jar 在 war 檔案的類別路徑上可用。
一個更簡單的解決方案是跳過parentContextKey並使用contextConfigLocation參數從每個 war 載入共用的 Bean 定義檔。這樣,每個 war 都將擁有每個共用 Bean 的自己的實例。對於簡單的無狀態 Bean,例如典型的服務,這實際上是一個很好的解決方案。
但是,為每個 war 實例化每個共用 Bean 的新實例可能會帶來一些缺點:一個常見的例子是建立 Hibernate SessionFactory。這通常是一個昂貴的過程,因此為了防止您的啟動時間失控,所描述的解決方案將確保僅執行一次。擁有 SessionFactory 的單一實例的另一個優點是它可以安全地充當第二層快取:您不希望在單一應用程式中有多個副本在周圍閒逛!一般來說,如果您有應該真正用作單例(在 Spring 意義上,即每個應用程式一個實例,而不是每個 JVM 一個實例)的有狀態 Bean,則應將它們定義在其他上下文可以存取的單一上下文中。
我包含了範例,包括 ear 檔案 和 原始碼。為了上傳 ear,我必須給它一個 .zip 副檔名:請在部署之前將檔案重新命名為 .ear!。原始碼實際上是一個 Eclipse 工作區,因此您可以輕鬆匯入和查看它(它需要 WTP 並且配置為 Spring IDE)。所需的所有 Spring jar 都已包含在內。在您部署應用程式後,前往 URL /web1 和 /web2 以查看第一個和第二個 war 檔案中 Servlet 的輸出。toString()服務的 將證明 war 確實使用了共用服務的同一個實例。
關於此功能的最佳資訊在 Spring 出色的 API 文件中:請查看 ContextLoader.loadParentContext 方法和 SingletonBeanFactoryLocator 類別的 JavaDoc。這些文件包含有關如何配置 web.xml 以及如何編寫 beanRefFactory.xml 的更多資訊。