ApplicationContext
的其他功能
如章節簡介中所討論,org.springframework.beans.factory
套件提供了管理和操作 Bean 的基本功能,包括以程式化的方式。org.springframework.context
套件新增了 ApplicationContext
介面,除了擴展其他介面以提供更面向應用程式框架風格的其他功能之外,它還擴展了 BeanFactory
介面。許多人以完全宣告式的方式使用 ApplicationContext
,甚至不以程式化的方式建立它,而是依賴諸如 ContextLoader
之類的支援類別,以在 Jakarta EE Web 應用程式的正常啟動過程中自動實例化 ApplicationContext
。
為了以更面向框架的風格增強 BeanFactory
功能,context 套件還提供了以下功能:
-
透過
MessageSource
介面,存取 i18n 風格的訊息。 -
透過
ResourceLoader
介面,存取資源,例如 URL 和檔案。 -
事件發布,即發布到實作
ApplicationListener
介面的 Bean,透過使用ApplicationEventPublisher
介面。 -
載入多個(階層式)Context,讓每個 Context 都專注於一個特定的層,例如應用程式的 Web 層,透過
HierarchicalBeanFactory
介面。
使用 MessageSource
進行國際化
ApplicationContext
介面擴展了一個名為 MessageSource
的介面,因此提供了國際化 (“i18n”) 功能。Spring 也提供了 HierarchicalMessageSource
介面,它可以階層式地解析訊息。這些介面共同提供了 Spring 實現訊息解析的基礎。在這些介面上定義的方法包括:
-
String getMessage(String code, Object[] args, String default, Locale loc)
:從MessageSource
檢索訊息的基本方法。當找不到指定地區設定的訊息時,將使用預設訊息。使用標準函式庫提供的MessageFormat
功能,傳入的任何引數都會成為替換值。 -
String getMessage(String code, Object[] args, Locale loc)
:與前一個方法基本相同,但有一個區別:無法指定預設訊息。如果找不到訊息,則會拋出NoSuchMessageException
。 -
String getMessage(MessageSourceResolvable resolvable, Locale locale)
:先前方法中使用的所有屬性也都封裝在名為MessageSourceResolvable
的類別中,您可以將其與此方法一起使用。
當 ApplicationContext
載入時,它會自動搜尋在 Context 中定義的 MessageSource
Bean。該 Bean 必須具有名稱 messageSource
。如果找到這樣的 Bean,則對先前方法的所有呼叫都委派給訊息來源。如果找不到訊息來源,則 ApplicationContext
會嘗試尋找包含具有相同名稱的 Bean 的父 Context。如果找到,它會將該 Bean 用作 MessageSource
。如果 ApplicationContext
找不到任何訊息來源,則會實例化一個空的 DelegatingMessageSource
,以便能夠接受對上述方法的呼叫。
Spring 提供了三個 MessageSource
實作:ResourceBundleMessageSource
、ReloadableResourceBundleMessageSource
和 StaticMessageSource
。它們都實作了 HierarchicalMessageSource
以進行巢狀訊息傳遞。StaticMessageSource
很少使用,但提供了以程式化方式將訊息新增到來源的方法。以下範例顯示了 ResourceBundleMessageSource
:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
該範例假設您在類別路徑中定義了三個資源套件,分別名為 format
、exceptions
和 windows
。任何解析訊息的請求都以 JDK 標準方式處理,透過 ResourceBundle
物件解析訊息。為了範例的目的,假設上述兩個資源套件檔案的內容如下:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
下一個範例顯示了一個程式來執行 MessageSource
功能。請記住,所有 ApplicationContext
實作也是 MessageSource
實作,因此可以轉換為 MessageSource
介面。
-
Java
-
Kotlin
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("message", null, "Default", Locale.ENGLISH)
println(message)
}
上述程式的結果輸出如下:
Alligators rock!
總之,MessageSource
在名為 beans.xml
的檔案中定義,該檔案位於您的類別路徑的根目錄。messageSource
Bean 定義透過其 basenames
屬性引用了許多資源套件。傳遞到 basenames
屬性的列表中的三個檔案作為檔案存在於您的類別路徑的根目錄,分別名為 format.properties
、exceptions.properties
和 windows.properties
。
下一個範例顯示了傳遞給訊息查找的引數。這些引數會轉換為 String
物件,並插入到查找訊息中的佔位符中。
<beans>
<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
-
Java
-
Kotlin
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}
class Example {
lateinit var messages: MessageSource
fun execute() {
val message = messages.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.ENGLISH)
println(message)
}
}
從 execute()
方法調用的結果輸出如下:
The userDao argument is required.
關於國際化 (“i18n”),Spring 的各種 MessageSource
實作遵循與標準 JDK ResourceBundle
相同的地區設定解析和回退規則。簡而言之,並繼續使用先前定義的範例 messageSource
,如果您想要針對英國 (en-GB
) 地區設定解析訊息,您將分別建立名為 format_en_GB.properties
、exceptions_en_GB.properties
和 windows_en_GB.properties
的檔案。
通常,地區設定解析由應用程式的周圍環境管理。在以下範例中,手動指定了針對其解析(英國)訊息的地區設定:
# in exceptions_en_GB.properties argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
-
Java
-
Kotlin
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.UK)
println(message)
}
上述程式運行的結果輸出如下:
Ebagum lad, the 'userDao' argument is required, I say, required.
您也可以使用 MessageSourceAware
介面來取得對已定義的任何 MessageSource
的參考。在 ApplicationContext
中定義的任何實作 MessageSourceAware
介面的 Bean,在建立和組態 Bean 時,都會注入應用程式 Context 的 MessageSource
。
由於 Spring 的 MessageSource 基於 Java 的 ResourceBundle ,因此它不會合併具有相同基本名稱的套件,而只會使用找到的第一個套件。具有相同基本名稱的後續訊息套件將被忽略。 |
作為 ResourceBundleMessageSource 的替代方案,Spring 提供了 ReloadableResourceBundleMessageSource 類別。此變體支援相同的套件檔案格式,但比基於標準 JDK 的 ResourceBundleMessageSource 實作更靈活。特別是,它允許從任何 Spring 資源位置(不僅僅是從類別路徑)讀取檔案,並支援套件屬性檔案的熱重新載入(同時在兩次重新載入之間有效地快取它們)。有關詳細資訊,請參閱 ReloadableResourceBundleMessageSource Javadoc。 |
標準和自訂事件
ApplicationContext
中的事件處理是透過 ApplicationEvent
類別和 ApplicationListener
介面提供的。如果將實作 ApplicationListener
介面的 Bean 部署到 Context 中,則每次將 ApplicationEvent
發布到 ApplicationContext
時,都會通知該 Bean。本質上,這是標準的觀察者設計模式。
從 Spring 4.2 開始,事件基礎架構已得到顯著改進,並提供了基於註解的模型,以及發布任何任意事件的能力(即,不一定從 ApplicationEvent 擴展的物件)。當發布這樣的物件時,我們會將其包裝在事件中。 |
下表描述了 Spring 提供的標準事件:
事件 | 說明 |
---|---|
|
當 |
|
當 |
|
當 |
|
當 |
|
一個 Web 特定事件,告知所有 Bean HTTP 請求已得到服務。此事件在請求完成後發布。此事件僅適用於使用 Spring 的 |
|
|
您也可以建立和發布自己的自訂事件。以下範例顯示了一個簡單的類別,它擴展了 Spring 的 ApplicationEvent
基底類別:
-
Java
-
Kotlin
public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)
若要發布自訂 ApplicationEvent
,請在 ApplicationEventPublisher
上呼叫 publishEvent()
方法。通常,這是透過建立一個實作 ApplicationEventPublisherAware
的類別並將其註冊為 Spring Bean 來完成的。以下範例顯示了這樣一個類別:
-
Java
-
Kotlin
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}
class EmailService : ApplicationEventPublisherAware {
private lateinit var blockedList: List<String>
private lateinit var publisher: ApplicationEventPublisher
fun setBlockedList(blockedList: List<String>) {
this.blockedList = blockedList
}
override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) {
this.publisher = publisher
}
fun sendEmail(address: String, content: String) {
if (blockedList!!.contains(address)) {
publisher!!.publishEvent(BlockedListEvent(this, address, content))
return
}
// send email...
}
}
在組態時,Spring 容器偵測到 EmailService
實作了 ApplicationEventPublisherAware
,並自動呼叫 setApplicationEventPublisher()
。實際上,傳入的參數是 Spring 容器本身。您正在透過其 ApplicationEventPublisher
介面與應用程式 Context 互動。
若要接收自訂 ApplicationEvent
,您可以建立一個實作 ApplicationListener
的類別,並將其註冊為 Spring Bean。以下範例顯示了這樣一個類別:
-
Java
-
Kotlin
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
lateinit var notificationAddress: String
override fun onApplicationEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
請注意,ApplicationListener
使用您的自訂事件類型(在前面的範例中為 BlockedListEvent
)進行泛型參數化。這表示 onApplicationEvent()
方法可以保持類型安全,從而避免任何向下轉型的需要。您可以註冊任意數量的事件監聽器,但請注意,預設情況下,事件監聽器會同步接收事件。這表示 publishEvent()
方法會阻塞,直到所有監聽器都完成事件處理。這種同步和單執行緒方法的一個優點是,當監聽器接收到事件時,如果交易 Context 可用,它會在發布者的交易 Context 內運作。如果需要另一種事件發布策略,例如,預設情況下的非同步事件處理,請參閱 Spring 的 ApplicationEventMulticaster
介面和 SimpleApplicationEventMulticaster
實作的 Javadoc,以取得可以應用於自訂 "applicationEventMulticaster" Bean 定義的組態選項。在這些情況下,ThreadLocal 和記錄 Context 不會針對事件處理進行傳播。有關可觀測性問題的更多資訊,請參閱 @EventListener
可觀測性章節。
以下範例顯示了用於註冊和組態上述每個類別的 Bean 定義:
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>
<value>[email protected]</value>
<value>[email protected]</value>
<value>[email protected]</value>
</list>
</property>
</bean>
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="[email protected]"/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
</bean>
總而言之,當呼叫 emailService
bean 的 sendEmail()
方法時,如果有任何電子郵件訊息應該被封鎖,則會發布類型為 BlockedListEvent
的自訂事件。 blockedListNotifier
bean 註冊為 ApplicationListener
並接收 BlockedListEvent
,此時它可以通知相關方。
Spring 的事件機制旨在用於相同應用程式內容中 Spring bean 之間的簡單通訊。然而,對於更複雜的企業整合需求,另行維護的 Spring Integration 專案提供了完整支援,以建構輕量級、模式導向、事件驅動架構,這些架構建立在廣為人知的 Spring 程式設計模型之上。 |
基於註解的事件監聽器
您可以使用 @EventListener
註解在受管 bean 的任何方法上註冊事件監聽器。 BlockedListNotifier
可以重寫如下
-
Java
-
Kotlin
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier {
lateinit var notificationAddress: String
@EventListener
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
方法簽章再次宣告它監聽的事件類型,但這次使用彈性的名稱,且無需實作特定的監聽器介面。事件類型也可以透過泛型來縮小範圍,只要實際的事件類型在其實作階層中解析您的泛型參數即可。
如果您的方法應該監聽多個事件,或者如果您想要定義它完全沒有參數,則事件類型也可以在註解本身上指定。以下範例示範如何執行此操作
-
Java
-
Kotlin
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
也可以透過使用註解的 condition
屬性來新增額外的執行階段篩選,該屬性定義了 SpEL
表達式,該表達式應符合以實際調用特定事件的方法。
以下範例示範如何重寫我們的通知器,使其僅在事件的 content
屬性等於 my-event
時才被調用
-
Java
-
Kotlin
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
每個 SpEL
表達式都會針對專用內容進行評估。下表列出了提供給內容的項目,以便您將它們用於條件式事件處理
名稱 | 位置 | 描述 | 範例 |
---|---|---|---|
事件 |
根物件 |
實際的 |
|
引數陣列 |
根物件 |
用於調用方法的引數(作為物件陣列)。 |
|
引數名稱 |
評估內容 |
特定方法引數的名稱。如果名稱不可用(例如,因為程式碼在編譯時沒有 `-parameters` 標記),則個別引數也可使用 |
|
請注意,即使您的方法簽章實際上是指已發布的任意物件,#root.event
也能讓您存取底層事件。
如果您需要發布事件作為處理另一個事件的結果,您可以變更方法簽章以傳回應該發布的事件,如下列範例所示
-
Java
-
Kotlin
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
非同步監聽器 不支援此功能。 |
handleBlockedListEvent()
方法會為其處理的每個 BlockedListEvent
發布新的 ListUpdateEvent
。如果您需要發布多個事件,您可以改為傳回 Collection
或事件陣列。
非同步監聽器
如果您希望特定監聽器非同步處理事件,您可以重複使用常規 @Async
支援。以下範例示範如何執行此操作
-
Java
-
Kotlin
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}
@EventListener
@Async
fun processBlockedListEvent(event: BlockedListEvent) {
// BlockedListEvent is processed in a separate thread
}
使用非同步事件時,請注意以下限制
-
如果非同步事件監聽器拋出
Exception
,則不會將其傳播給呼叫者。 有關更多詳細信息,請參閱AsyncUncaughtExceptionHandler
。 -
非同步事件監聽器方法無法透過傳回值來發布後續事件。如果您需要發布另一個事件作為處理結果,請注入
ApplicationEventPublisher
以手動發布事件。 -
預設情況下,ThreadLocals 和日誌記錄內容不會針對事件處理進行傳播。 有關可觀察性問題的更多資訊,請參閱
@EventListener
可觀察性章節。
排序監聽器
如果您需要先調用一個監聽器,然後再調用另一個監聽器,則可以將 @Order
註解新增至方法宣告中,如下列範例所示
-
Java
-
Kotlin
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
@EventListener
@Order(42)
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
泛型事件
您也可以使用泛型來進一步定義事件的結構。 考慮使用 EntityCreatedEvent<T>
,其中 T
是已建立的實際實體類型。 例如,您可以建立以下監聽器定義,以僅接收 Person
的 EntityCreatedEvent
-
Java
-
Kotlin
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
由於類型擦除,這僅在觸發的事件解析事件監聽器篩選的泛型參數時才有效(也就是類似 class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }
)。
在某些情況下,如果所有事件都遵循相同的結構(如前例中的事件所示),這可能會變得非常繁瑣。 在這種情況下,您可以實作 ResolvableTypeProvider
以引導框架超越執行階段環境提供的內容。 以下事件示範如何執行此操作
-
Java
-
Kotlin
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
override fun getResolvableType(): ResolvableType? {
return ResolvableType.forClassWithGenerics(javaClass, ResolvableType.forInstance(getSource()))
}
}
這不僅適用於 ApplicationEvent ,也適用於您作為事件傳送的任何任意物件。 |
最後,與傳統的 ApplicationListener
實作一樣,實際的多播發生在執行階段透過內容範圍的 ApplicationEventMulticaster
進行。 預設情況下,這是一個 SimpleApplicationEventMulticaster
,在呼叫者執行緒中進行同步事件發布。 這可以透過 "applicationEventMulticaster" bean 定義來取代/自訂,例如,用於非同步處理所有事件和/或處理監聽器例外狀況
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
方便存取低階資源
為了最佳使用和理解應用程式內容,您應該熟悉 Spring 的 Resource
抽象,如 資源 中所述。
應用程式內容是 ResourceLoader
,可用於載入 Resource
物件。 Resource
本質上是 JDK java.net.URL
類別的功能更豐富的版本。 實際上,Resource
的實作在適當情況下會包裝 java.net.URL
的實例。 Resource
可以從幾乎任何位置以透明的方式取得低階資源,包括從類別路徑、檔案系統位置、任何可使用標準 URL 描述的位置以及其他一些變體。 如果資源位置字串是沒有任何特殊前綴的簡單路徑,則這些資源的來源對於實際的應用程式內容類型是特定的且適當的。
您可以配置部署到應用程式內容中的 bean 來實作特殊的回呼介面 ResourceLoaderAware
,以便在初始化時自動回呼,並將應用程式內容本身作為 ResourceLoader
傳入。 您還可以公開 Resource
類型的屬性,用於存取靜態資源。 它們會像任何其他屬性一樣注入到其中。 您可以將這些 Resource
屬性指定為簡單的 String
路徑,並在部署 bean 時依賴從這些文字字串到實際 Resource
物件的自動轉換。
提供給 ApplicationContext
建構子的位置路徑或路徑實際上是資源字串,並且以簡單的形式,會根據特定的內容實作進行適當處理。 例如,ClassPathXmlApplicationContext
將簡單的位置路徑視為類別路徑位置。 您也可以使用帶有特殊前綴的位置路徑(資源字串)來強制從類別路徑或 URL 載入定義,而與實際的內容類型無關。
應用程式啟動追蹤
ApplicationContext
管理 Spring 應用程式的生命週期,並圍繞元件提供豐富的程式設計模型。 因此,複雜的應用程式可能具有同樣複雜的元件圖和啟動階段。
使用特定指標追蹤應用程式啟動步驟可以幫助了解啟動階段的時間花費在哪裡,但它也可以用作更好地理解整個內容生命週期的一種方式。
AbstractApplicationContext
(及其子類別)已使用 ApplicationStartup
進行檢測,後者會收集有關各種啟動階段的 StartupStep
資料
-
應用程式內容生命週期(基本套件掃描、組態類別管理)
-
bean 生命週期(實例化、智慧初始化、後處理)
-
應用程式事件處理
以下是 AnnotationConfigApplicationContext
中檢測的範例
-
Java
-
Kotlin
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
// create a startup step and start recording
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages)
// end the current step
scanPackages.end()
應用程式內容已使用多個步驟進行檢測。 記錄後,可以使用特定工具收集、顯示和分析這些啟動步驟。 有關現有啟動步驟的完整清單,您可以查看專用附錄章節。
預設的 ApplicationStartup
實作是一個 no-op 變體,以實現最小的額外負荷。 這表示預設情況下,在應用程式啟動期間不會收集任何指標。 Spring Framework 隨附一個使用 Java Flight Recorder 追蹤啟動步驟的實作:FlightRecorderApplicationStartup
。 若要使用此變體,您必須在建立 ApplicationContext
後立即將其實例配置給它。
開發人員也可以使用 ApplicationStartup
基礎架構,如果他們提供自己的 AbstractApplicationContext
子類別,或者他們希望收集更精確的資料。
ApplicationStartup 僅用於應用程式啟動期間和核心容器; 這絕不是 Java 分析器或指標程式庫(如 Micrometer)的替代品。 |
若要開始收集自訂 StartupStep
,元件可以直接從應用程式內容取得 ApplicationStartup
實例,讓其元件實作 ApplicationStartupAware
,或在任何注入點要求 ApplicationStartup
類型。
開發人員在建立自訂啟動步驟時不應使用 "spring.*" 命名空間。 此命名空間保留供 Spring 內部使用,並且可能會變更。 |
適用於 Web 應用程式的便捷 ApplicationContext 實例化
您可以使用宣告方式建立 ApplicationContext
實例,例如,使用 ContextLoader
。 當然,您也可以使用其中一個 ApplicationContext
實作以程式設計方式建立 ApplicationContext
實例。
您可以使用 ContextLoaderListener
註冊 ApplicationContext
,如下列範例所示
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
監聽器會檢查 contextConfigLocation
參數。 如果參數不存在,監聽器會使用 /WEB-INF/applicationContext.xml
作為預設值。 當參數存在時,監聽器會使用預先定義的分隔符號(逗號、分號和空白字元)分隔 String
,並將這些值用作搜尋應用程式內容的位置。 也支援 Ant 樣式的路徑模式。 範例為 /WEB-INF/*Context.xml
(適用於名稱以 Context.xml
結尾且位於 WEB-INF
目錄中的所有檔案)和 /WEB-INF/**/*Context.xml
(適用於 WEB-INF
任何子目錄中的所有此類檔案)。
將 Spring ApplicationContext
部署為 Jakarta EE RAR 檔案
可以將 Spring ApplicationContext
部署為 RAR 檔案,將內容及其所有必要的 bean 類別和程式庫 JAR 封裝在 Jakarta EE RAR 部署單元中。 這相當於啟動獨立的 ApplicationContext
(僅託管在 Jakarta EE 環境中),能夠存取 Jakarta EE 伺服器設施。 RAR 部署是部署無頭 WAR 檔案情境的更自然替代方案 — 實際上,WAR 檔案沒有任何 HTTP 進入點,僅用於在 Jakarta EE 環境中啟動 Spring ApplicationContext
。
RAR 部署非常適合不需要 HTTP 進入點,而是僅包含訊息端點和排程工作的應用程式內容。 此內容中的 Bean 可以使用應用程式伺服器資源,例如 JTA 交易管理員和 JNDI 繫結的 JDBC DataSource
實例和 JMS ConnectionFactory
實例,也可以向平台的 JMX 伺服器註冊 — 所有這些都透過 Spring 的標準交易管理和 JNDI 和 JMX 支援設施。 應用程式元件也可以透過 Spring 的 TaskExecutor
抽象與應用程式伺服器的 JCA WorkManager
互動。
有關 RAR 部署中涉及的組態詳細資訊,請參閱 SpringContextResourceAdapter
類別的 javadoc。
為了簡單地將 Spring ApplicationContext 部署為 Jakarta EE RAR 檔案
-
將所有應用程式類別封裝到 RAR 檔案中(這是具有不同副檔名的標準 JAR 檔案)。
-
將所有必要的程式庫 JAR 新增至 RAR 歸檔的根目錄。
-
新增
META-INF/ra.xml
部署描述符(如SpringContextResourceAdapter
的 javadoc 中所示)和對應的 Spring XML bean 定義檔案(通常為META-INF/applicationContext.xml
)。 -
將產生的 RAR 檔案放入應用程式伺服器的部署目錄中。
此類 RAR 部署單元通常是獨立的。 它們不會將元件暴露給外部世界,甚至不會暴露給同一應用程式的其他模組。 與基於 RAR 的 ApplicationContext 的互動通常透過它與其他模組共用的 JMS 目的地發生。 例如,基於 RAR 的 ApplicationContext 也可能會排程一些工作或對檔案系統中的新檔案做出反應(或類似情況)。 如果需要允許從外部進行同步存取,它可以(例如)匯出 RMI 端點,同一機器上的其他應用程式模組可以使用這些端點。 |