遙遙領先
VMware 提供培訓和認證來加速您的進步。
瞭解更多上個月我在土耳其做了一個 Core Spring 培訓。課程結束時,我討論了一個應用程式的架構,一些參與者將在完成課程後構建該應用程式。此應用程式將包含一個 ear 檔案,其中包含多個 war 檔案,並且提出了一個問題,是否可以定義一個單一的 ApplicationContext,它可以作為所有 war 檔案的 WebApplicationContext 的共享父級。此上下文將包含服務、DAO 和其他非特定於單個 Web 模組的 bean 定義。
實際上,Spring 使這件事變得非常容易,但是課程和參考手冊都沒有詳細解釋如何從 Web 應用程式中使用此功能。因此,我編寫了一個簡短的示例應用程式來說明它是如何工作的,我將在我的第一篇部落格文章中對此進行討論。
在一個典型的 Spring Web 應用程式中,您使用 ContextLoaderListener(或者,如果您使用的是 Servlet 2.2 / 2.3 容器,則使用 ContextLoaderServlet)來引導 WebApplicationContext。您可以透過 web.xml 中的上下文引數來配置此類使用的 ContextLoader。如果您使用過此功能,您可能熟悉contextConfigLocation引數,該引數允許您指定哪些檔案構成要構造的 WebApplicationContext。
事實證明,還有另一個引數可以用來以宣告方式獲得所需的功能:parentContextKey。使用此引數,您可以指示 ContextLoader 使用另一個名為 ContextSingletonBeanFactoryLocator 的類來搜尋一個 bean,該 bean 的名稱由parentContextKey的值指定,該值定義在一個名稱與特定模式匹配的配置檔案中。預設情況下,此模式為“classpath*:beanRefContext.xml”,表示 classpath 上的所有名為 beanRefContext 的檔案。(對於普通的 SingletonBeanFactoryLocator,它是“classpath*:beanRefFactory.xml”)這個 bean 必須是 ApplicationContext 本身,並且此上下文將成為 ContextLoader 建立的 WebApplicationContext 的父上下文。但是,如果此上下文已經存在,則將使用該上下文,並且不會建立新的上下文(因此名稱為 SingletonBeanFactoryLocator)。
讓我們看看這意味著什麼:首先,我們需要在 ear 中單獨的 jar,其中包含服務、DAO 等的程式碼。在此 jar 中,我們放置一個 beanRefContext.xml 檔案,該檔案包含 ApplicationContext 的單個 bean 定義。通常,這將是一個 ClassPathXmlApplicationContext。然後,該 bean 定義將引用一個或多個“常規”bean 配置檔案,這些檔案包含 war 檔案中的程式碼要使用的服務 bean 和其他內容; 像這樣
<!-- 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 檔案的 classpath 上可用。
一個更簡單的解決方案是跳過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()服務的 toString() 將證明 wars 確實使用共享服務的同一例項。
有關此功能的最佳資訊位於 Spring 的出色 API 文件中:請檢視 ContextLoader.loadParentContext 方法和 SingletonBeanFactoryLocator 類的 JavaDoc。 這些文件包含有關如何配置 web.xml 以及如何編寫 beanRefFactory.xml 的更多資訊。