新聞中心
核心事件
ApplicationContext中的事件處理是通過ApplicationEvent類和ApplicationListener接口提供的。如果將實現(xiàn)一個Bean實現(xiàn)了ApplicationListener接口,那么每當ApplicationEvent發(fā)布到ApplicationContext時,就會通知該bean。本質(zhì)上,這是標準的觀察者設計模式。

目前累計服務客戶1000+,積累了豐富的產(chǎn)品開發(fā)及服務經(jīng)驗。以網(wǎng)站設計水平和技術(shù)實力,樹立企業(yè)形象,為客戶提供成都網(wǎng)站設計、網(wǎng)站建設、網(wǎng)站策劃、網(wǎng)頁設計、網(wǎng)絡營銷、VI設計、網(wǎng)站改版、漏洞修補等服務。成都創(chuàng)新互聯(lián)始終以務實、誠信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過對領先技術(shù)的掌握、對創(chuàng)意設計的研究、對客戶形象的視覺傳遞、對應用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進步。
到Spring 4.2為止,事件基礎設施得到了顯著改進,提供了基于注釋的模型以及發(fā)布任意事件的能力(也就是說,不一定是從ApplicationEvent擴展的對象)。當這樣的對象被發(fā)布時,我們將它包裝在一個事件中。
下表列出了Spring提供的標準事件:
|
Event |
Explanation |
|
ContextRefreshedEvent |
在ApplicationContext被初始化或刷新時發(fā)布(例如,通過使用ConfigurableApplicationContext接口上的refresh()方法)。這里的“初始化”意味著加載了所有bean,檢測并激活了后處理器bean,預實例化了單例,并且ApplicationContext對象已經(jīng)準備好可以使用。只要上下文沒有關閉,就可以多次觸發(fā)刷新,前提是所選擇的ApplicationContext實際上支持這種“熱”刷新。 |
|
ContextStartedEvent |
通過使用ConfigurableApplicationContext接口上的start()方法啟動ApplicationContext時發(fā)布。在這里,“started”意味著所有生命周期bean都接收一個顯式的開始信號。通常,該信號用于在顯式停止之后重新啟動bean,但也可以用于啟動未配置為自動啟動的組件(例如,在初始化時尚未啟動的組件)。 |
|
ContextStoppedEvent |
當通過使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext時發(fā)布。在這里,“stopped”意味著所有生命周期bean都接收一個明確的停止信號。停止的上下文可以通過start()調(diào)用重新啟動。 |
|
ContextClosedEvent |
使用ConfigurableApplicationContext接口上的close()方法或通過JVM關閉掛鉤關閉ApplicationContext時發(fā)布。在這里,“closed”意味著所有的單例bean都會被銷毀。一旦上下文被關閉,它就會到達生命的終點,并且不能被刷新或重啟。 |
|
RequestHandledEvent |
一個特定于web的事件,告訴所有bean一個HTTP請求已經(jīng)得到了服務。此事件在請求完成后發(fā)布。這個事件只適用于使用Spring的DispatcherServlet的web應用程序。 |
|
ServletRequestHandledEvent |
RequestHandledEvent的一個子類,用于添加特定于servlet的上下文信息。 |
以上事件發(fā)布時機:
- ContextRefreshedEvent
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() {
// ...
finishRefresh();
}
}
- ContextStartedEvent
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() {
// ...
finishRefresh();
}
protected void finishRefresh() {
// 初始化LifecycleProcessor(DefaultLifecycleProcessor)
initLifecycleProcessor();
getLifecycleProcessor().onRefresh();
}
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
}
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
public void start() {
startBeans(false);
this.running = true;
}
}
- ContextStoppedEvent
該事件與上面的started是對應的
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
}
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
public void stop() {
stopBeans();
this.running = false;
}
}
- ContextClosedEvent
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
}
}
protected void doClose() {
publishEvent(new ContextClosedEvent(this));
}
}
- ServletRequestHandledEvent
public abstract class FrameworkServlet {
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) {
publishRequestHandledEvent(request, response, startTime, failureCause);
}
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
}你還可以創(chuàng)建和發(fā)布自己的自定義事件。下面的例子展示了一個簡單的類,它擴展了Spring的ApplicationEvent基類:
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;
}
}要發(fā)布自定義的ApplicationEvent,需要調(diào)用ApplicationEventPublisher的publishEvent()方法。通常,這是通過創(chuàng)建一個實現(xiàn)ApplicationEventPublisherAware的類并將其注冊為Spring bean來完成的。下面的例子展示了這樣一個類:
public class EmailService implements ApplicationEventPublisherAware {
private List blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List 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;
}
}
} 在配置時,Spring容器檢測到EmailService實現(xiàn)了ApplicationEventPublisherAware,并自動調(diào)用
setApplicationEventPublisher()。實際上,傳入的參數(shù)是Spring容器本身。通過ApplicationEventPublisher接口與應用程序上下文進行交互。
要接收自定義的ApplicationEvent,可以創(chuàng)建一個實現(xiàn)ApplicationListener的類,并將其注冊為Spring bean。下面的例子展示了這樣一個類:
public class BlockedListNotifier implements ApplicationListener{
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
}
}
請注意,ApplicationListener通常參數(shù)化為自定義事件的類型。這意味著onApplicationEvent()方法可以保持類型安全,避免任何向下轉(zhuǎn)換的需要。你可以注冊任意數(shù)量的事件監(jiān)聽器,但請注意,默認情況下,事件監(jiān)聽器是同步接收事件的。這意味著publishEvent()方法會阻塞,直到所有監(jiān)聽器都完成事件處理。這種同步和單線程方法的一個優(yōu)點是,當偵聽器接收到事件時,如果事務上下文可用,它將在發(fā)布者的事務上下文內(nèi)操作。
通過注解監(jiān)聽事件
可以使用@EventListener注解在托管bean的任何方法上注冊事件監(jiān)聽器??梢詫lockedListNotifier重寫為:
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
}
}同時監(jiān)聽多個事件
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
}異步事件
如果希望特定的監(jiān)聽器異步處理事件,可以重用常規(guī)的@Async支持。如下面的例子所示:
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
}
在使用異步事件時,請注意以下限制:
- 如果異步事件監(jiān)聽器拋出異常,則異常不會傳播到調(diào)用者。
- 異步事件監(jiān)聽器方法無法通過返回值發(fā)布后續(xù)事件。如果你需要發(fā)布另一個事件作為處理的結(jié)果,注入一個ApplicationEventPublisher來手動發(fā)布事件。
事件監(jiān)聽順序
如果需要在調(diào)用另一個監(jiān)聽器之前調(diào)用一個監(jiān)聽器,可以在方法聲明中添加@Order注解,如下面的例子所示:
@EventListener
@Order(1)
public void processBlockedListEvent(BlockedListEvent event) {
}
通用的事件
你還可以使用泛型來進一步定義事件的結(jié)構(gòu)??紤]使用EntityCreatedEvent
@EventListener
public void onPersonCreated(EntityCreatedEventevent) {
}
由于類型擦除,只有當觸發(fā)的事件解析了事件監(jiān)聽器過濾的泛型參數(shù)(即類似于PersonCreatedEvent類擴展EntityCreatedEvent
事件觸發(fā)原理
方式1:ApplicationEventPublisher
AbstractApplicationContext實現(xiàn)了ApplicationEventPublisher接口,那么只要ApplicationContext繼承自AbstractApplicationContext都可以直接發(fā)布事件:
public abstract class AbstractApplicationContext {
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent>) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
}方式2:通過@EventListener注解
該注解是由EventListenerMethodProcessor處理器處理的。
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 獲取容器中注冊的事件監(jiān)聽工廠,可以有多個
Map beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
}
public void afterSingletonsInstantiated() {
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
processBean(beanName, type);
}
}
private void processBean(final String beanName, final Class> targetType) {
Map annotatedMethods = null;
try {
// 取得Bean內(nèi)帶有@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
// 遍歷找到的所有@EventListener注解的方法
for (Method method : annotatedMethods.keySet()) {
// 遍歷所有的EventListenerFactory
for (EventListenerFactory factory : factories) {
// 判斷當前的事件監(jiān)聽工廠是否支持當前的方法
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 創(chuàng)建對應的事件監(jiān)聽程序
ApplicationListener> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
}
} 文章名稱:Spring容器啟動過程中發(fā)布的核心事件及事件處理機制詳解
鏈接URL:http://fisionsoft.com.cn/article/coigijh.html


咨詢
建站咨詢
