在多 war Spring 應用程式中使用共享父應用程式上下文

工程 | Joris Kuipers | 2007 年 6 月 11 日 | ...

上個月我在土耳其進行了一次 Spring 核心培訓。課程結束時,我討論了一個應用程式的架構,一些參與者在完成課程後將要構建這個應用程式。這個應用程式將包含一個 ear 檔案和幾個 war 檔案,並且提出了一個問題,是否有可能定義一個單獨的 ApplicationContext,它可以作為所有 war 檔案的 WebApplicationContexts 的共享父級。這個上下文將包含服務、DAO 和其他不特定於單個 web 模組的 bean 定義。

實際上,Spring 很容易就能做到這一點,但是課程和參考手冊都沒有詳細解釋如何在您的 Web 應用程式中使用此功能。因此,我編寫了一個簡短的示例應用程式來演示其工作原理,我將在我的第一篇部落格文章中討論它。

ContextLoader 和 SingletonBeanFactoryLocator 類

在典型的 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>

最後,我們需要透過在每個 war 的 MANIFEST.MF 檔案中使用Class-Path條目,使此 jar 在 war 檔案的類路徑上可用。

為什麼要使用它?

一個更簡單的解決方案是跳過parentContextKey,並使用contextConfigLocation引數從每個 war 中載入共享 bean 定義檔案。這樣,每個 war 都將擁有自己的每個共享 bean 例項。對於簡單的無狀態 bean,例如典型的服務,這實際上是一個很好的解決方案。

但是,為每個 war 例項化每個共享 bean 的新例項可能會有幾個缺點:一個常見的例子是建立 Hibernate SessionFactory。這通常是一個昂貴的過程,因此為了防止您的啟動時間失控,所描述的解決方案將確保只執行一次此操作。擁有 SessionFactory 的單個例項的另一個優點是它可以安全地充當二級快取:您不希望在單個應用程式中存在多個副本!通常,如果您有應該真正用作單例的有狀態 bean(在 Spring 意義上,即每個應用程式一個例項而不是每個 JVM 一個例項),您應該將它們定義在由其他上下文訪問的單個上下文中。

示例

我已將示例包含在內,既有 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 的更多資訊。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有