1.15 ApplicationContext
的其它功能
像在這個章節討論的,org.springframework.beans.factory
包提供基本的管理和操作bean的功能,包含編程式方式。org.springframework.context
包添加ApplicationContext
接口,它拓展BeanFactory
接口,此外還擴展了其他接口以提供更多面嚮應用程序框架的樣式的附加功能。許多人使用ApplicationContext
以完全聲明的方式,甚至沒有以編程方式創建它,但是取而代之的是依靠諸如ContextLoader
之類的支持類來自動實例化ApplicationContext
,這是Java EE Web應用程序正常啟動過程的一部分。
為了以更加面向框架的方式增強BeanFactory
的功能,上下文包還提供以下功能:
- 通過
MessageSource
接口獲取,在i18n
獲取消息 - 通過
ResourceLoader
接口,獲取資源,例如URL和文件 - 通過使用
ApplicationEventPublisher
接口,將事件發佈到實現ApplicationListener
接口的bean - 加載多個(分層)上下文,通過
HierarchicalBeanFactory
接口將每個上下文集中在一個特定層上,例如應用程序的Web層
1.15.1 使用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
被加載時,它會自動地在上下文中搜索bean定義為MessageSource
的類。這個bean必須有個messageSource
名字。如果bean沒有找到,所有調用前面的方法被代理到消息源。如果沒有找到消息源,ApplicationContext
嘗試去父容器查找相同名稱的bean。如果找到,則使用它作為MessageSource
。如果ApplicationContext
沒有找到任何消息源,一個空的DelegatingMessageSource
被實例化去接受前面定義的方法調用。
Spring提供兩個MessageSource
實現,ResourceBundleMessageSource
和StaticMessageSource
。兩者都實現HierarchicalMessageSource
以便進行嵌套消息傳遞。StaticMessageSource
很少被使用,但是提供編程式的方式去添加消息源。下面例子展示:
<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
實現也是MessageSources
實現並且能夠轉換為MessageSource
接口。
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);
}
上面的程序輸出結果是:
Alligators rock!
總而言之,MessageSource
被定義在叫做beans.xml
的文件中,它存在於你的類路徑root下。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>
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);
}
}
調用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.
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);
}
上面的程序輸出結果如下:
Ebagum lad, the 'userDao' argument is required, I say, required.
你也可以使用MessageSourceAware
接口去獲取一個引用任意已經被定義的MessageSource
。任何在ApplicationContext
中定義的bean,當MessageSource
bean被創建且被配置時,實現MessageSourceAware
接口將被注入應用上下文的MessageSource
。
作為
ResourceBundleMessageSource
的替代方法,Spring提供一個ReloadableResourceBundleMessageSource
類。這個變體支持相同包文件格式,但是比基於標準JDK的ResourceBundleMessageSource
實現更靈活。特別是,它允許從任何Spring資源為主讀取文件並且支持包屬性文件的熱加載(同時在它們之間有效地緩存它們)。查看ReloadableResourceBundleMessageSource
javadoc詳細信息。代碼示例:
com.liyong.ioccontainer.starter.MessageSourceIocContainer
1.15.2 標準和自定義事件
在ApplicationContext
中事件處理是通過ApplicationEvent
和ApplicationListener
接口提供的。如果bean實現ApplicationListener
接口並且部署到上下文中,則每次將ApplicationEvent
發佈到ApplicationContext
時,都會通知該bean。實質上,這是一個標準的觀察者模式。
從Spring4.2開始,事件基礎設施已經被顯著地改善並且提供基於註解的模式以及去發佈任意事件的能力(也就是說,對象沒有必須要從
ApplicationEvent
拓展)。當發佈一個對象時,我們包裝為事件。
下面表格描述Spring提供的標準事件:
Event | Explanation |
---|---|
ContextRefreshedEvent |
當ApplicationContext 被初始化或刷新(例如,通過在ConfigurableApplicationContext 接口上使用refresh()方法)時發送。這裡的“initialized ”意思是所有的bean被加載、post-processor (後置處理器)bean被檢查和激活、單例bean前置實例化和ApplicationContext 對象已經準備好使用。只要上下文沒有被關閉,刷新能夠在多個時間觸發,前提是所選的ApplicationContext 實際上支持這種“熱 ”刷新。例如,XmlWebApplicationContext 支持熱刷新,但是GenericApplicationContext 不支持。 |
ContextStartedEvent |
當ApplicationContext 通過在ConfigurableApplicationContext 接口上使用start() 方法啟動時發送。這裡的“started ”意思是所有Lifecyclebean 接收一個真實的開始信號。通常地,這個信號在顯示停止之後重啟bean使用,但也可以用於啟動尚未配置自動啟動的組件(例如,尚未在初始化時啟動的組件) |
ContextStoppedEvent |
當ApplicationContext 通過在ConfigurableApplicationContext 上接口上使用stop() 方法停止時發佈。這裡的“stopped ”意思是所有Lifecycle 的bean接受一個顯示停止信號。停止上下文可以通過調用start() 重啟。 |
ContextClosedEvent |
當ApplicationContext 通過在ConfigurableApplicationContext 接口上使用close() 關閉或者通過JVM關閉鉤子時發佈。這裡的"closed "意思是所有單例bean將被銷燬。一旦上下文被關閉,它已達到使用壽命,無法刷新或重新啟動。 |
RequestHandledEvent |
一個特定於Web的事件,告訴所有Bean HTTP請求已得到服務。這個時間在請求完成之後被髮布。這個事件僅僅能應用到使用Spring的DispatcherServlet 的web應用中。 |
ServletRequestHandledEvent |
RequestHandledEvent 的子類,用於添加特定於Servlet 的上下文信息。 |
你也可以創建和發佈你自己的自定義事件。下面例子展示了一個簡單類,它拓展了Spring的ApplicationEvent
基礎類:
public class BlackListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlackListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
去發佈自定義ApplicationEvent
,在ApplicationEventPublisher
上調用publishEvent()
方法。通常地,通過創建一個類實現ApplicationEventPublisherAware
接口並且把它註冊為Spring的bean。下面的例子展示:
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blackList;
private ApplicationEventPublisher publisher;
public void setBlackList(List<String> blackList) {
this.blackList = blackList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blackList.contains(address)) {
publisher.publishEvent(new BlackListEvent(this, address, content));
return;
}
// send email...
}
}
在配置時,Spring容器檢測EmailService
實現ApplicationEventPublisherAware
並且自動地調用setApplicationEventPublisher()
方法。事實上,傳入的參數是Spring容器本身。你正在通過其ApplicationEventPublisher
接口與應用程序上下文進行交互。
去接受自定義ApplicationEvent
,你可以創建一個類實現ApplicationListener
並且註冊它作為Spirng的bean。下面例子展示以一個類:
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
注意,ApplicationListener
通常是用定製事件的類型參數化的(在前面的例子中是BlackListEvent
)。也就是說onApplicationEvent()
方法能保持類型安全,避免任何轉換。你可以註冊許多你希望的事件監聽,但是注意,默認情況,事件監聽接受事件時同步地。也就是說publishEvent()
方法阻塞直到所有監聽器完成事件處理。這種同步和單線程方法的一個優點是,當監聽器接收到事件時,如果事務上下文可用,它將在發佈者的事務上下文中進行操作。
如果有必要採用其他發佈事件的策略,查看javadoc對應Spring的ApplicationEventMulticaster
接口和SimpleApplicationEventMulticaster
實現配置。
下面例子顯示bean定義使用去註冊和配置每個類;
<bean id="emailService" class="example.EmailService">
<property name="blackList">
<list>
<value>[email protected]</value>
<value>[email protected]</value>
<value>[email protected]</value>
</list>
</property>
</bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
<property name="notificationAddress" value="[email protected]"/>
</bean>
放到一起,當emailService
bean的sendEmail()
方法被調用時,如果這裡任何郵件信息需要被例入黑名單,一個類型為BlackListEvent
自定義事件被髮布。blackListNotifier
bean作為ApplicationListener
和接受BlackListEvent
被註冊,在這一點上,它可以通知有關各方。
Spring的事件機制被設計為在同一個應用上下文中Spring bean之間的簡單通信/交流。然而,對於更復雜的企業集成需求,單獨維護的
Spring integration
項目提供了對構建輕量級、面向模式、事件驅動的體系結構的完整支持,這些體系結構構建於著名的Spring編程模型之上。
基於註解事件監聽器
從Spring4.2後,你可以在任何通過使用@EventListener
註解的bean,公共方法註冊一個事件監聽器。BlackListNotifier
可以被重寫,像下面例子:
public class BlackListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
這個方法簽名再次聲明它監聽的事件類型,但是,在這裡一個靈活的名字和不需要實現特定監聽器接口。只要實際事件類型解析了實現層次結構中的泛型參數,就可以通過泛型縮小事件類型。
如果你的方法需要監聽一些事件或者如果你想去定義它為無參數,事件類型也可以在註解自身上指定。下面例子展示怎樣去做:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
也可以通過使用定義SpEL表達式的註釋的condition屬性來添加其他運行時過濾器,該註釋應匹配以針對特定事件實際調用該方法。
以下示例顯示了僅當事件的content屬性等於my-event
時,才可以重寫我們的通知程序以進行調用:
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
每個SpEL表達式都會根據專用上下文進行評估。下表列出了上下文可用的項,以便你可以將它們用於條件事件處理。
Name | Location | Description | Example |
---|---|---|---|
Event | root object | 實際的 ApplicationEvent . |
#root.event 或 event
|
Arguments array | root object | 這些參數(對象數組)被使用去調用方法 |
#root.args 或args ; args[0] 獲取第一個元素. |
Argument name | evaluation context | 任何方法參數名。如,因為一些原因,這些名稱無效(例如,因為在編譯字節碼中沒有debug信息),使用#a <#arg> 語法也可以使用單個參數,其中<#arg> 代表參數索引(從0開始) |
#blEvent 或 #a0 (你可以使用 #p0 或#p<#arg>`參數符號作為別名) |
請注意,即使你的方法簽名實際上引用了已發佈的任意對象,#root.event也允許你可以訪問底層事件。
如果你需要發佈一個事件作為處理其它事件結果,你可以改變方法簽名去返回事件,類似下面例子:
@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
這個特性對於異步監聽是不支持的。
這個新方式通過上面的方法為每個BlackListEvent
處理髮佈一個新ListUpdateEvent
。如果你需要去發佈一些事件,你可以返回一個事件Collection
。
異步事件監聽器
如果你想一個特定監聽器去處理異步事件,你可以重用常規的@Async
支持。下面例子展示怎樣使用:
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
// BlackListEvent is processed in a separate thread
}
使用異步事件時,請注意以下限制:
- 如果一個異步事件監聽器拋出
Exception
,它不會被傳遞到調用者。查看AsyncUncaughtExceptionHandler
更多詳情。 - 異步事件監聽器方法不能通過返回一個值發佈一個後續事件。如果你需要發佈處理結果的其它事件,注入
ApplicationEventPublisher
去手動發佈事件。
監聽器順序
如果你需要一個監聽器調用在另外一個監聽器之前,你可以添加@Order
註解到方法聲明上,類似下面例子:
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
泛型事件
你可以使用泛型去進一步定義你的事件結構。考慮使用EntityCreatedEvent<T>
,其中T
是已創建的實際實體的類型。例如,你可以創建下面的監聽器定義,為Person
去接受EntityCreatedEvent
:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
由於類型擦除,只有在觸發的事件解析事件監聽器過濾器所基於的通用參數(即,類似於類PersonCreatedEvent
擴展EntityCreatedEvent<Person>{})
的情況下才可以工作。
在某些情況下,如果所有事件都遵循相同的結構,這可能會變得很乏味(就像前面示例中的事件一樣)。在這種情況下,你可以實現ResolvableTypeProvider
來指導框架,使其超出運行時環境提供的範圍(備註:通過ResolvableTypeProvider
提供更多類相關信息)。下面的事件說明了如何做到這一點:
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()));
}
}
這不僅適用於
ApplicationEvent
,而且適用於你作為事件發送的任何任意對象。
1.15.3 便捷地訪問低級別資源
為了更好的使用和理解應用上下文,你應該熟悉Spring的Resource
抽象,在Resources中描述。
應用上下文是ResourceLoader
,它可以被使用加載Resource
對象。Resource
本質上是JDK java.net.URL
類的功能更豐富的版本。事實上,Resource
實現包裝一個java.net.URL實例,Resource
可以透明的方式從幾乎任何位置獲取低級資源,包含從類路徑、文件系統路徑、任何描述一個標準URL、以及其他的變體。如果資源位置字符串是沒有任何特殊前綴的簡單路徑,那麼這些資源的來源是特定的,並且適合於實際的應用程序上下文類型。
你可以配置部署到應用程序上下文中的Bean,以實現特殊的回調接口ResourceLoaderAware
,以便在初始化時自動回調,並將應用程序上下文本身作為ResourceLoader
傳入。你也可以暴露Resource
類型屬性,被使用獲取靜態資源。它們像其他屬性一樣注入其中。當bean被部署時,你可以指定這些Resource
屬性作為簡單String路徑並且依靠這些文本字符串自動轉換真實Resource
對象。
提供給ApplicationContext
構造函數的位置路徑實際上是資源字符串,並且以簡單的形式根據特定的上下文實現進行適當的處理。例如,ClassPathXmlApplicationContext
將簡單的位置路徑視為類路徑位置。你也可以使用帶有特殊前綴的位置路徑(資源字符串)來強制從類路徑或URL中加載定義,而不管實際的上下文類型如何。
1.15.4 Web應用的ApplicationContext實例化
你可以通過聲明式地創建ApplicationContext
實例,例如:ApplicationContext
。當然,你也可以通過使用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
作為默認值。當參數存在時,監聽器通過使用預先定義的分隔符和使用值作為應用上下文搜索的路徑的分隔字符串。Ant格式路徑模式也支持很好。示例包括/WEB-INF/*Context.xml
(適用於所有名稱以Context.xml
結尾且位於WEB-INF
目錄中的文件)和/WEB-INF/**/*Context.xml
(適用於所有此類文件)文件在WEB-INF
的任何子目錄中)。
1.15.5 部署SpringApplicationContext
作為Java EE rar文件
可以將Spring ApplicationContext
部署為RAR文件,並將上下文及其所有必需的bean類和庫JAR封裝在Java EE RAR部署單元中。這等效於引導獨立的ApplicationContext
(僅託管在Java EE環境中)能夠訪問Java EE服務器功能。RAR部署是部署無頭WAR文件的方案的一種更自然的選擇,實際上,這種WAR文件沒有任何HTTP入口點,僅用於在Java EE環境中引導Spring ApplicationContext
。RAR部署非常適合於不需要HTTP入口點,而只由消息端點和調度任務組成的應用程序上下文。在上下文中的這些bean能使用應用服務資源,例如,JTA事物管理、JNDI綁定JDBC DataSource
實例、JMS ConnectionFactory
實例和註冊平臺的JMX服務-通過所有Spring的標準事物管理、JNDI、JMX支持的設施。應用組件也可以通過Spring的TaskExecutor
抽象與應用server的JCA WorkManager
相互交互 。
有關RAR部署中涉及的配置詳細信息,請參見SpringContextResourceAdapter類的javadoc。
對於將Spring ApplicationContext
作為Java 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端點,這些端點可以由同一臺機器上的其他應用程序模塊使用。
1.16 BeanFactory
BeanFactory
API為Spring IoC工廠提供底層基礎。它的特定契約主要用於與Spring的其他部分和相關第三方框架的集成,它的DefaultListableBeanFactory
實現是更高級別的GenericApplicationContext
容器中的一個關鍵代理。
BeanFactory
和相關的接口(例如:BeanFactoryAware
、InitializingBean
、DisposableBean
)是為其他框架組件非常重要的集成點。通過不需要任何註解,甚至不需要反射,它們可以在容器及其組件之間進行非常有效的交互。應用級別bean可能使用相同回調接口,但通常更喜歡通過註釋或通過編程配置進行聲明式依賴注入。
請注意,核心BeanFactory
API級別及其DefaultListableBeanFactory
實現不對配置格式或要使用的任何組件註釋進行假設。所有這些風格都通過擴展(比如XmlBeanDefinitionReader
和AutowiredAnnotationBeanPostProcessor
)實現,並在共享的BeanDefinition
對象上進行操作,作為核心元數據表示。這是使Spring的容器如此靈活和可擴展的本質所在。
1.16.1 BeanFactory
或ApplicationContext
本節說明BeanFactory
和ApplicationContext
容器級別之間的區別以及對引導的影響。
除非有充分的理由,否則應使用ApplicationContext
,除非將GenericApplicationContext
和其子類AnnotationConfigApplicationContext
作為自定義引導的常見實現,否則應使用ApplicationContext
。這些是用於所有常見目的的Spring核心容器的主要入口點:加載配置文件、觸發類路徑掃描、以編程方式註冊Bean定義和帶註解的類,以及(從5.0版本開始)註冊功能性Bean定義。
因為ApplicationContext
包含BeanFactory
的所有功能,所以通常建議在普通BeanFactory中使用,除非需要完全控制Bean處理的方案。在ApplicationContext
(例如GenericApplicationContext
實現)中,按照約定(即,按bean名稱或按bean類型(尤其是後處理器))檢測到幾種bean,而普通的DefaultListableBeanFactory
則與任何特殊bean無關。
對於許多擴展的容器功能,例如註解處理和AOP代理,BeanPostProcessor
擴展點是必不可少的。如果僅使用普通的DefaultListableBeanFactory
,則默認情況下不會檢測到此類後處理器並將其激活。這種情況可能會造成混淆,因為你的bean配置實際上並沒有錯。而是在這種情況下,需要通過其他設置完全引導容器。
下表列出了BeanFactory
和ApplicationContext
接口和實現提供的功能。
Feature | BeanFactory |
ApplicationContext |
---|---|---|
Bean 實例化/連接 | Yes | Yes |
集成生命週期管理 | No | Yes |
BeanPostProcessor 自動註冊 |
No | Yes |
BeanFactoryPostProcessor 自動註冊 |
No | Yes |
便捷的MessageSource 獲取(國際化) |
No | Yes |
內建ApplicationEvent 發佈機制 |
No | Yes |
要向DefaultListableBeanFactory
顯式註冊Bean後處理器,需要以編程方式調用addBeanPostProcessor
,如以下示例所示:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions
// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());
// now start using the factory
要將BeanFactoryPostProcessor
應用於普通的DefaultListableBeanFactory
,你需要調用其postProcessBeanFactory
方法,如以下示例所示:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);
在這兩種情況下,顯式的註冊步驟都不方便,這就是為什麼在Spring支持的應用程序中,各種ApplicationContext
變量比普通的DefaultListableBeanFactory
更為可取的原因,尤其是在典型企業設置中依賴BeanFactoryPostProcessor
和BeanPostProcessor
實例來擴展容器功能時。
AnnotationConfigApplicationContext
已註冊了所有常見的註解後處理器,並且可以通過配置註解(例如@EnableTransactionManagement
)在幕後引入其他處理器。在Spring基於註解的配置模型的抽象級別上,bean後處理器的概念僅是內部容器詳細信息。
作者
個人從事金融行業,就職過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就職於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公眾號和博客站點對知識體系進行分享。
博客地址: http://youngitman.tech
CSDN: https://blog.csdn.net/liyong1028826685
微信公眾號:
技術交流群: