深入淺出Spring/SpringBoot 事件監(jiān)聽機(jī)制
說明
事件監(jiān)聽機(jī)制可以理解為是一種觀察者模式,有數(shù)據(jù)發(fā)布者(事件源)和數(shù)據(jù)接受者(監(jiān)聽器);
在Java中,事件對象都是繼承java.util.EventObject對象,事件監(jiān)聽器都是java.util.EventListener實(shí)例;
EventObject對象不提供默認(rèn)構(gòu)造器,需要外部傳遞source參數(shù),即用于記錄并跟蹤事件的來源;
Spring事件
Spring事件對象為ApplicationEvent,繼承EventObject,源碼如下:
- public abstract class ApplicationEvent extends EventObject {
- /**
- * Create a new ApplicationEvent.
- * @param source the object on which the event initially occurred (never {@code null})
- */
- public ApplicationEvent(Object source) {
- super(source);
- this.timestamp = System.currentTimeMillis();
- }
- }
Spring事件監(jiān)聽器為ApplicationListener,繼承EventListener, 源碼如下:
- public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
- void onApplicationEvent(E var1);
- }
實(shí)現(xiàn)Spring事件監(jiān)聽有兩種方式:
- 面向接口編程,實(shí)現(xiàn)ApplicationListener接口;
- 基于注解驅(qū)動,@EventListener(Spring自定義的注解);
實(shí)例:
面向接口編程,實(shí)現(xiàn)ApplicationListener接口:
自定義事件對象:
- public class MyApplicationEvent extends ApplicationEvent {
- public MyApplicationEvent(Object source) {
- super(source);
- }
- }
自定義事件監(jiān)聽器:
- public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
- @Override
- public void onApplicationEvent(MyApplicationEvent event) {
- System.out.println("收到事件:" + event);
- }
- }
啟動服務(wù)并發(fā)布事件:
- public class ApplicationEventBootstrap {
- public static void main(String[] args) {
- AnnotationConfigApplicationContext context =
- new AnnotationConfigApplicationContext();
- // 注冊自定義事件監(jiān)聽器
- context.addApplicationListener(new MyApplicationListener());
- // 啟動上下文
- context.refresh();
- // 發(fā)布事件,事件源為Context
- context.publishEvent(new MyApplicationEvent(context));
- // 結(jié)束
- context.close();
- }
- }
運(yùn)行結(jié)果:
- 收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]
使用注解 @EventListener實(shí)現(xiàn)Spring事件監(jiān)聽:
- @Component
- public class MyApplicationListener2 {
- @EventListener(MyApplicationEvent.class)
- public void onEvent(MyApplicationEvent event) {
- System.out.println("收到事件:" + event);
- }
- }
啟動并發(fā)布事件:
- public class ApplicationEventBootstrap {
- public static void main(String[] args) {
- AnnotationConfigApplicationContext context =
- new AnnotationConfigApplicationContext();
- // 注冊自定義事件監(jiān)聽器
- context.register(MyApplicationListener2.class);
- // 啟動上下文
- context.refresh();
- // 發(fā)布事件,事件源為Context
- context.publishEvent(new MyApplicationEvent(context));
- // 結(jié)束
- context.close();
- }
- }
運(yùn)行結(jié)果:
- 收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]
通過實(shí)例可以看出,上面兩種方式都可正常發(fā)布和接收事件。
實(shí)現(xiàn)原理
通過上面實(shí)例可以看出,context 可以發(fā)布事件,那底層是怎么發(fā)布的,讓我們繼續(xù)看源碼:
- public abstract class AbstractApplicationContext extends DefaultResourceLoader
- implements ConfigurableApplicationContext {
- protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
- ...
- getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
- ...
- }
- }
通過源碼我們可以看出,事件應(yīng)該是通過
ApplicationEventMulticaster發(fā)布的,我們繼續(xù)看:
- public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster
Spring 中事件發(fā)布都是通過
SimpleApplicationEventMulticaster來實(shí)現(xiàn)的
- public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
- ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
- for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
- Executor executor = getTaskExecutor();
- if (executor != null) {
- // 異步
- executor.execute(() -> invokeListener(listener, event));
- }
- else {
- invokeListener(listener, event);
- }
- }
- }
可以看出,如果設(shè)置了Executor則異步發(fā)送,否則同步;而且可以看出通過 resolveDefaultEventType(event) 對發(fā)布的事件類型進(jìn)行了校驗,這就是為什么我們可以直接使用泛型來指定我們想接收的事件對象, 比如上面的 ApplicationListener。
- private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
- try {
- listener.onApplicationEvent(event);
最后就使用對應(yīng)的ApplicationListener進(jìn)行接收和處理就行了,那么ApplicationListener是什么時候注冊的呢?
如何添加ApplicationListener?
- 直接添加,使用content.addApplicationListener(上面實(shí)例中有使用);
- 將自定義的ApplicationListener注冊為一個Bean,Spring再初始化Bean之后會添加,具體代碼在ApplicationListenerDetector#postProcessAfterInitialization,判斷一個Bean如果是ApplicationListener,則也是使用context.addApplicationListener添加;
- 使用注解@EventListener,在初始化Bean之后,會在EventListenerMethodProcessor中進(jìn)行處理和添加;
第三種實(shí)現(xiàn)的源碼如下(
EventListenerMethodProcessor中):
- private void processBean(final String beanName, final Class<?> targetType) {
- ....
- // 獲取public 且有@EventListener的方法
- AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
- ...
- ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
- // 添加監(jiān)聽器
- context.addApplicationListener(applicationListener);
- }
Spring內(nèi)建事件
- ContextRefreshedEvent: Spring應(yīng)用上下文就緒事件;
- ContextStartedEvent: Spring應(yīng)用上下文啟動事件;
- ContextStopedEvent: Spring應(yīng)用上下文停止事件;
- ContextClosedEvent: Spring應(yīng)用上下文關(guān)閉事件;
Spring Boot事件
Spring Boot事件是在Spring事件基礎(chǔ)上進(jìn)行的封裝
- public abstract class SpringApplicationEvent extends ApplicationEvent
事件對象改為SpringApplicationEvent,事件源為SpringApplication(Spring事件源為Context);
底層發(fā)布事件還是使用
SimpleApplicationEventMulticaster 對象,不過有點(diǎn)需要說明的是,Spring Boot 1.4開始,SpringApplication和ApplicationContext使用的都是
SimpleApplicationEventMulticaster實(shí)例,但是兩者屬于不同的對象(1.0 ~ 1.3版本是同一個對象);
事件回顧:
- public class EventBootstrap {
- public static void main(String[] args) {
- new SpringApplicationBuilder(Object.class)
- .listeners(event -> {
- System.out.println("事件對象:"
- + event.getClass().getSimpleName()
- + " ,事件源:" + event.getSource().getClass().getSimpleName());
- })
- .web(WebApplicationType.NONE)
- .run(args)
- .close();
- }
- }
運(yùn)行結(jié)果:
- 事件對象:ApplicationContextInitializedEvent ,事件源:SpringApplication
- 事件對象:ApplicationPreparedEvent ,事件源:SpringApplication
- 事件對象:ContextRefreshedEvent ,事件源:AnnotationConfigApplicationContext
- 事件對象:ApplicationStartedEvent ,事件源:SpringApplication
- 事件對象:ApplicationReadyEvent ,事件源:SpringApplication
- 事件對象:ContextClosedEvent ,事件源:AnnotationConfigApplicationContext
從結(jié)果可以看出,事件對象類型和事件源,以及事件發(fā)布順序。