偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring容器啟動(dòng)過(guò)程中發(fā)布的核心事件及事件處理機(jī)制詳解

開(kāi)發(fā) 項(xiàng)目管理
到Spring 4.2為止,事件基礎(chǔ)設(shè)施得到了顯著改進(jìn),提供了基于注釋的模型以及發(fā)布任意事件的能力(也就是說(shuō),不一定是從ApplicationEvent擴(kuò)展的對(duì)象)。當(dāng)這樣的對(duì)象被發(fā)布時(shí),我們將它包裝在一個(gè)事件中。

核心事件

ApplicationContext中的事件處理是通過(guò)ApplicationEvent類和ApplicationListener接口提供的。如果將實(shí)現(xiàn)一個(gè)Bean實(shí)現(xiàn)了ApplicationListener接口,那么每當(dāng)ApplicationEvent發(fā)布到ApplicationContext時(shí),就會(huì)通知該bean。本質(zhì)上,這是標(biāo)準(zhǔn)的觀察者設(shè)計(jì)模式。

到Spring 4.2為止,事件基礎(chǔ)設(shè)施得到了顯著改進(jìn),提供了基于注釋的模型以及發(fā)布任意事件的能力(也就是說(shuō),不一定是從ApplicationEvent擴(kuò)展的對(duì)象)。當(dāng)這樣的對(duì)象被發(fā)布時(shí),我們將它包裝在一個(gè)事件中。

下表列出了Spring提供的標(biāo)準(zhǔn)事件:

Event

Explanation

ContextRefreshedEvent

在ApplicationContext被初始化或刷新時(shí)發(fā)布(例如,通過(guò)使用ConfigurableApplicationContext接口上的refresh()方法)。這里的“初始化”意味著加載了所有bean,檢測(cè)并激活了后處理器bean,預(yù)實(shí)例化了單例,并且ApplicationContext對(duì)象已經(jīng)準(zhǔn)備好可以使用。只要上下文沒(méi)有關(guān)閉,就可以多次觸發(fā)刷新,前提是所選擇的ApplicationContext實(shí)際上支持這種“熱”刷新。

ContextStartedEvent

通過(guò)使用ConfigurableApplicationContext接口上的start()方法啟動(dòng)ApplicationContext時(shí)發(fā)布。在這里,“started”意味著所有生命周期bean都接收一個(gè)顯式的開(kāi)始信號(hào)。通常,該信號(hào)用于在顯式停止之后重新啟動(dòng)bean,但也可以用于啟動(dòng)未配置為自動(dòng)啟動(dòng)的組件(例如,在初始化時(shí)尚未啟動(dòng)的組件)。

ContextStoppedEvent

當(dāng)通過(guò)使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext時(shí)發(fā)布。在這里,“stopped”意味著所有生命周期bean都接收一個(gè)明確的停止信號(hào)。停止的上下文可以通過(guò)start()調(diào)用重新啟動(dòng)。

ContextClosedEvent

使用ConfigurableApplicationContext接口上的close()方法或通過(guò)JVM關(guān)閉掛鉤關(guān)閉ApplicationContext時(shí)發(fā)布。在這里,“closed”意味著所有的單例bean都會(huì)被銷毀。一旦上下文被關(guān)閉,它就會(huì)到達(dá)生命的終點(diǎn),并且不能被刷新或重啟。

RequestHandledEvent

一個(gè)特定于web的事件,告訴所有bean一個(gè)HTTP請(qǐng)求已經(jīng)得到了服務(wù)。此事件在請(qǐng)求完成后發(fā)布。這個(gè)事件只適用于使用Spring的DispatcherServlet的web應(yīng)用程序。

ServletRequestHandledEvent

RequestHandledEvent的一個(gè)子類,用于添加特定于servlet的上下文信息。

以上事件發(fā)布時(shí)機(jī):

  • 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是對(duì)應(yīng)的

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ā)布自己的自定義事件。下面的例子展示了一個(gè)簡(jiǎn)單的類,它擴(kuò)展了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()方法。通常,這是通過(guò)創(chuàng)建一個(gè)實(shí)現(xiàn)ApplicationEventPublisherAware的類并將其注冊(cè)為Spring bean來(lái)完成的。下面的例子展示了這樣一個(gè)類:

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;
}
}
}

在配置時(shí),Spring容器檢測(cè)到EmailService實(shí)現(xiàn)了ApplicationEventPublisherAware,并自動(dòng)調(diào)用
setApplicationEventPublisher()。實(shí)際上,傳入的參數(shù)是Spring容器本身。通過(guò)ApplicationEventPublisher接口與應(yīng)用程序上下文進(jìn)行交互。

要接收自定義的ApplicationEvent,可以創(chuàng)建一個(gè)實(shí)現(xiàn)ApplicationListener的類,并將其注冊(cè)為Spring bean。下面的例子展示了這樣一個(gè)類:

public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {


private String notificationAddress;


public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}


public void onApplicationEvent(BlockedListEvent event) {
}
}

請(qǐng)注意,ApplicationListener通常參數(shù)化為自定義事件的類型。這意味著onApplicationEvent()方法可以保持類型安全,避免任何向下轉(zhuǎn)換的需要。你可以注冊(cè)任意數(shù)量的事件監(jiān)聽(tīng)器,但請(qǐng)注意,默認(rèn)情況下,事件監(jiān)聽(tīng)器是同步接收事件的。這意味著publishEvent()方法會(huì)阻塞,直到所有監(jiān)聽(tīng)器都完成事件處理。這種同步和單線程方法的一個(gè)優(yōu)點(diǎn)是,當(dāng)偵聽(tīng)器接收到事件時(shí),如果事務(wù)上下文可用,它將在發(fā)布者的事務(wù)上下文內(nèi)操作。

通過(guò)注解監(jiān)聽(tīng)事件

可以使用@EventListener注解在托管bean的任何方法上注冊(cè)事件監(jiān)聽(tīng)器??梢詫lockedListNotifier重寫為:

public class BlockedListNotifier {


private String notificationAddress;


public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}


@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
}
}

同時(shí)監(jiān)聽(tīng)多個(gè)事件

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
}

異步事件

如果希望特定的監(jiān)聽(tīng)器異步處理事件,可以重用常規(guī)的@Async支持。如下面的例子所示:

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
}

在使用異步事件時(shí),請(qǐng)注意以下限制:

  1. 如果異步事件監(jiān)聽(tīng)器拋出異常,則異常不會(huì)傳播到調(diào)用者。
  2. 異步事件監(jiān)聽(tīng)器方法無(wú)法通過(guò)返回值發(fā)布后續(xù)事件。如果你需要發(fā)布另一個(gè)事件作為處理的結(jié)果,注入一個(gè)ApplicationEventPublisher來(lái)手動(dòng)發(fā)布事件。

事件監(jiān)聽(tīng)順序

如果需要在調(diào)用另一個(gè)監(jiān)聽(tīng)器之前調(diào)用一個(gè)監(jiān)聽(tīng)器,可以在方法聲明中添加@Order注解,如下面的例子所示:

@EventListener
@Order(1)
public void processBlockedListEvent(BlockedListEvent event) {
}

通用的事件

你還可以使用泛型來(lái)進(jìn)一步定義事件的結(jié)構(gòu)??紤]使用EntityCreatedEvent<T>,其中T是實(shí)際創(chuàng)建的實(shí)體的類型。例如,你可以創(chuàng)建以下監(jiān)聽(tīng)器定義,只接收Person的EntityCreatedEvent:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
}

由于類型擦除,只有當(dāng)觸發(fā)的事件解析了事件監(jiān)聽(tīng)器過(guò)濾的泛型參數(shù)(即類似于PersonCreatedEvent類擴(kuò)展EntityCreatedEvent<Person>{…})時(shí),才會(huì)起作用。

事件觸發(fā)原理

方式1:ApplicationEventPublisher

AbstractApplicationContext實(shí)現(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:通過(guò)@EventListener注解

該注解是由EventListenerMethodProcessor處理器處理的。

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 獲取容器中注冊(cè)的事件監(jiān)聽(tīng)工廠,可以有多個(gè)
Map<String, EventListenerFactory> 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<Method, EventListener> annotatedMethods = null;
try {
// 取得Bean內(nèi)帶有@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
// 遍歷找到的所有@EventListener注解的方法
for (Method method : annotatedMethods.keySet()) {
// 遍歷所有的EventListenerFactory
for (EventListenerFactory factory : factories) {
// 判斷當(dāng)前的事件監(jiān)聽(tīng)工廠是否支持當(dāng)前的方法
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 創(chuàng)建對(duì)應(yīng)的事件監(jiān)聽(tīng)程序
ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
}
}


責(zé)任編輯:武曉燕 來(lái)源: 實(shí)戰(zhàn)案例錦集
相關(guān)推薦

2009-09-02 18:34:28

C#鼠標(biāo)事件

2011-07-01 14:14:34

Qt 事件

2011-07-01 14:20:59

Qt 事件

2023-02-23 08:15:33

Spring異常處理機(jī)制

2011-03-17 09:20:05

異常處理機(jī)制

2023-06-15 14:09:00

解析器Servlet容器

2011-09-05 17:35:18

MTK啟動(dòng)過(guò)程RTOS

2013-08-07 14:48:00

HTML5

2011-06-28 13:27:13

ARM Linux

2010-03-05 15:40:16

Python異常

2023-09-07 10:31:27

2010-09-16 09:37:21

JavaScript事

2023-09-14 15:15:36

2015-11-06 13:59:01

JavaScript事件處理

2021-09-28 15:03:06

Linux內(nèi)核arm

2017-01-11 18:44:43

React Nativ觸摸事件Android

2011-07-04 14:38:43

QT Qevent

2009-08-04 13:53:58

C#委托類C#事件

2021-03-02 09:12:25

Java異常機(jī)制

2019-05-27 14:43:49

Tomcat架構(gòu)部署
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)