ApplicationContext 的附加功能
正如章節介紹中所討論的,org.springframework.beans.factory 包提供了管理和操作 Bean 的基本功能,包括以程式設計方式。org.springframework.context 包添加了 ApplicationContext 介面,它擴充套件了 BeanFactory 介面,此外還擴充套件了其他介面,以提供更面向應用框架風格的額外功能。許多人以完全宣告的方式使用 ApplicationContext,甚至不以程式設計方式建立它,而是依賴於諸如 ContextLoader 這樣的支援類,在 Jakarta EE Web 應用程式的正常啟動過程中自動例項化一個 ApplicationContext。
為了以更面向框架的風格增強 BeanFactory 功能,上下文包還提供了以下功能
-
透過
MessageSource介面以 i18n 風格訪問訊息。 -
透過
ResourceLoader介面訪問資源,例如 URL 和檔案。 -
透過使用
ApplicationEventPublisher介面釋出事件,即釋出給實現ApplicationListener介面的 Bean。 -
透過
HierarchicalBeanFactory介面載入多個(分層)上下文,讓每個上下文專注於一個特定的層,例如應用程式的 Web 層。
使用 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 時,它會自動搜尋上下文中定義的 MessageSource Bean。該 Bean 必須具有名稱 messageSource。如果找到這樣的 Bean,所有對上述方法的呼叫都將委託給訊息源。如果找不到訊息源,ApplicationContext 會嘗試查詢包含同名 Bean 的父級。如果找到,它將使用該 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 的引用。任何在實現 MessageSourceAware 介面的 ApplicationContext 中定義的 Bean,在 Bean 建立和配置時都會注入應用程式上下文的 MessageSource。
由於 Spring 的 MessageSource 基於 Java 的 ResourceBundle,它不會合並具有相同基本名稱的捆綁包,而只會使用找到的第一個捆綁包。後續具有相同基本名稱的訊息捆綁包將被忽略。 |
作為 ResourceBundleMessageSource 的替代方案,Spring 提供了 ReloadableResourceBundleMessageSource 類。此變體支援相同的捆綁檔案格式,但比基於標準 JDK 的 ResourceBundleMessageSource 實現更靈活。特別是,它允許從任何 Spring 資源位置(不僅僅是類路徑)讀取檔案,並支援捆綁屬性檔案的熱過載(同時在期間高效快取它們)。有關詳細資訊,請參閱 ReloadableResourceBundleMessageSource 的 Javadoc。 |
標準和自定義事件
ApplicationContext 中的事件處理透過 ApplicationEvent 類和 ApplicationListener 介面提供。如果一個實現 ApplicationListener 介面的 Bean 被部署到上下文中,那麼每次 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 介面與應用程式上下文進行互動。
要接收自定義 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() 方法會阻塞,直到所有監聽器都處理完事件。這種同步和單執行緒方法的一個優點是,當監聽器接收到事件時,如果存在事務上下文,它會在釋出者的事務上下文中操作。如果需要另一種事件釋出策略,例如預設情況下非同步事件處理,請參閱 Spring 的 ApplicationEventMulticaster 介面和 SimpleApplicationEventMulticaster 實現的 javadoc,以獲取可應用於自定義“applicationEventMulticaster”Bean 定義的配置選項。在這些情況下,ThreadLocal 和日誌上下文不會傳播到事件處理。有關可觀測性問題的更多資訊,請參閱@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...
}
}
不要將此類 Bean 定義為延遲載入,因為 ApplicationContext 將遵循該定義,並且不會註冊該方法以偵聽事件。 |
方法簽名再次聲明瞭它偵聽的事件型別,但這次使用了一個靈活的名稱,並且沒有實現特定的監聽器介面。事件型別也可以透過泛型進行縮小,只要實際事件型別在其實現層次結構中解析您的泛型引數即可。
如果您的方法應偵聽多個事件,或者您想將其定義為不帶任何引數,則事件型別也可以在註解本身上指定。以下示例展示瞭如何實現
-
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 表示式都在專用上下文中進行評估。下表列出了上下文中可用的專案,以便您可以將它們用於條件事件處理
| 名稱 | 位置 | 描述 | 示例 |
|---|---|---|---|
事件 |
根物件 |
實際的 |
|
引數陣列 |
根物件 |
用於呼叫方法的引數(作為物件陣列)。 |
|
引數名稱 |
評估上下文 |
特定方法引數的名稱。如果名稱不可用(例如,因為程式碼在沒有 |
|
請注意,即使您的方法簽名實際引用了已釋出的任意物件,#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以手動釋出事件。 -
預設情況下,ThreadLocal 和日誌上下文不會傳播到事件處理。有關可觀測性問題的更多資訊,請參閱
@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 來實現特殊的 callback 介面 ResourceLoaderAware,以便在初始化時自動呼叫該 Bean,並將應用程式上下文字身作為 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
try (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);
}
// create a startup step and start recording
try (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);
}
應用程式上下文已經透過多個步驟進行了儀表化。一旦記錄,這些啟動步驟就可以用特定工具進行收集、顯示和分析。有關現有啟動步驟的完整列表,您可以檢視專用附錄部分。
預設的 ApplicationStartup 實現是一個無操作變體,開銷最小。這意味著預設情況下,在應用程式啟動期間不會收集任何指標。Spring 框架提供了使用 Java Flight Recorder 跟蹤啟動步驟的實現:FlightRecorderApplicationStartup。要使用此變體,您必須在建立 ApplicationContext 後立即為其配置一個例項。
如果開發人員提供自己的 AbstractApplicationContext 子類,或者希望收集更精確的資料,也可以使用 ApplicationStartup 基礎設施。
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 檔案的一種更自然的替代方案——實際上,一個沒有 HTTP 入口點的 WAR 檔案,僅用於在 Jakarta EE 環境中引導 Spring ApplicationContext。
RAR 部署非常適合不需要 HTTP 入口點,而是僅由訊息端點和計劃作業組成的應用程式上下文。此類上下文中的 Bean 可以使用應用程式伺服器資源,例如 JTA 事務管理器以及 JNDI 繫結的 JDBC DataSource 例項和 JMS ConnectionFactory 例項,並且還可以透過 Spring 的標準事務管理和 JNDI 和 JMX 支援設施註冊到平臺的 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 端點,這些端點可以由同一機器上的其他應用程式模組使用。 |