事件機制做爲一種編程機制,在不少開發語言中都提供了支持,同時許多開源框架的設計中都使用了事件機制,好比SpringFramework。html
在 Java 語言中,Java 的事件機制參與者有3種角色:前端
1.Event Source:具體的事件源,好比說,你在界面點擊一個 button 按鈕,那麼這個按鈕就是事件源,要想使按鈕對某些事件進行響應,你就須要註冊特定的監聽器 listener,事件源將事件對象傳遞給全部註冊的監聽器;java
2.Event Object:事件狀態對象,用於監聽器的相應的方法之中,做爲參數;web
3.Event Listener:事件監聽器,當它監聽到 event object 產生的時候,它就調用相應的方法進行處理;spring
上面3個角色就定義了事件機制的基本模型。
數據庫
流程是:編程
1)首先咱們將監聽器對象註冊給事件源對象,這樣當事件觸發時系統即可以經過事件源訪問相應的監聽器;設計模式
2)當事件源觸發事件後,系統將事件的相關信息封裝成相應類型的事件對象,並將其發送給註冊到事件源的相應監聽器;緩存
3)當事件對象發送給監聽器後,系統調用監聽器相應的事件處理方法對事件進行處理,也就是作出響應;安全
PS:監聽器與事件源之間是「多對多」的關係。
下面對幾個概念進行詳細介紹:
1)事件源:事件最初由事件源產生,事件源能夠是 Java Bean 或由生成事件能力的對象。在 Java 中,每個組件會產生什麼樣的事件,已經被定義好了。或者說,對於任何一個事件來講,哪些組件能夠產生它,已是肯定的了。
2)事件對象:在事件對象中最高層是 java.util.EventObject,全部事件狀態對象都是繼承該類的派生類,好比 Spring-context 的事件機制的根類爲 ApplicationEvent,而 Apollo 配置中心的事件處理類 AppCreationEvent 是繼承自 Spring 的;
除了 EventObject是在 util 包中,其它都在 java.awt、java.awt.event 包或 java.swing、java.swing.event 包中,好比有 AWTEvent、ActionEvent、AdjustmentEvent等等;
public class EventObject implements java.io.Serializable { protected transient Object source; public EventObject(Object source) { if (source == null) throw new IllegalArgumentException("null source"); this.source = source; } public Object getSource() { return source; } public String toString() { return getClass().getName() + "[source=" + source + "]"; } }
public abstract class ApplicationEvent extends EventObject { private static final long serialVersionUID = 7099057708183571937L; private final long timestamp = System.currentTimeMillis(); public ApplicationEvent(Object source) { super(source); } public final long getTimestamp() { return this.timestamp; } }
public class AppCreationEvent extends ApplicationEvent { public AppCreationEvent(Object source) { super(source); } public App getApp() { Preconditions.checkState(source != null); return (App) this.source; } }
3) 監聽器對象:監聽器對象就是一個實現了特定監聽器接口的類的實例,在監聽器接口的最頂層接口是 java.util.EventListener,這個接口是全部事件偵聽器接口必須擴展的標記接口。感到詫異的是這個接口徹底是空的,裏面沒有任何的抽象方法的定義;
public interface EventListener { }
事件監聽器的接口命名方式爲:XXListener,並且,在java中,這些接口已經被定義好了。用來被實現,它定義了事件處理器(即事件處理的方法原型,這個方法須要被從新實現)。例如:ActionListener接口、MouseListener接口、WindowListener接口等;
說了這麼多,如今要回到正題來,說到 Spring 的事件機制,就得從 Spring 的容器開始提及,在 IOC 容器的啓動過程,當全部的 bean 都已經初始化處理完成以後,Spring IOC 容器會有一個發佈事件的動做。
public void refresh() throws BeansException, IllegalStateException { //來個鎖,否則 refresh() 還沒結束,你又來個啓動或銷燬容器的操做,那不就亂套了嘛 synchronized (this.startupShutdownMonitor) { .............. //初始化容器的信息源 initMessageSource(); //初始化事件監聽多路廣播器 initApplicationEventMulticaster(); //是個空殼方法,在AnnotationApplicationContex上下文中沒有實現,可能在spring後面的版本會去擴展。 onRefresh(); //註冊監聽器 registerListeners(); //對象的建立:初始化剩下全部的(非懶加載的)單實例對象 finishBeanFactoryInitialization(beanFactory); //刷新完成工做,包括初始化LifecycleProcessor,發佈刷新完成事件等 finishRefresh(); } ................. }
在 AbstractApplicationContext 類的 finishRefresh 方法,裏面就會發布(廣播)一條表明初始化結束的消息:
protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). clearResourceCaches(); // Initialize lifecycle processor for this context. initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); //發佈(廣播)一條消息,類型ContextRefreshedEvent表明Spring容器初始化結束 publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }
這樣,當 Spring IOC 容器加載處理完相應的 bean 以後,也給咱們提供了一個機會,能夠去作一些本身想作的事情。這也是 Spring IOC 容器提供給外部擴展的地方,咱們可使用這個擴展機制,來實現一些特殊的業務需求。
好比讓咱們的 bean 實現 ApplicationListener 接口,這樣當發佈事件時, Spring 的 IOC 容器就會以容器中的實例對象做爲事件源類,並從中找到事件的監聽者,此時會執行相應的方法好比 onApplicationEvent(E event),咱們的業務邏輯代碼就會寫在這個方法裏面。這樣咱們的目的就能夠達到了,但你這是可能會有一個疑問,這樣的代碼咱們能夠經過實現 InitializingBean 接口來實現啊,也會被 Spring 容器自動調用。但若是咱們這時的需求是咱們要作的事情,是必要要等到全部的 bean 都被處理完成以後再進行,此時 InitializingBean 接口就步合適了。(可查看下文案例3)
Spring 的事件機制是觀察者設計模式的實現,經過 ApplicationEvent 和 ApplicationListener 接口,能夠實現容器事件處理。
ApplicationListener 監聽 ApplicationEvent 及其子類的事件:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); }
若是容器中有一個監聽器,每當容器發佈事件時,監聽器將自動被觸發,固然這種事件機制必須須要程序顯示的觸發。
其中 Spring 有一些內置的事件,當完成某種操做時會發出某些事件動做。好比上文中的監聽 ContextRefreshedEvent 事件,當全部的bean都初始化完成併成功裝載後會觸發該事件,實現 ApplicationListener<ContextRefreshedEvent> 接口能夠收到監聽動做,如何實現本身的業務邏輯。
下面是一些 Spring 的內置事件:
ContextClosedEvent:當使用 ConfigurableApplicationContext 接口中的 close() 方法關閉 ApplicationContext 時,該事件被髮布。一個已關閉的上下文到達生命週期末端,它不能被刷新或重啓;【關閉容器會發布這個事件】
RequestHandledEvent:這是一個 web-specific 事件,告訴全部 bean HTTP 請求已經被服務。只能應用於使用 DispatcherServlet 的 Web 應用。在使用 Spring 做爲前端的MVC控制器時,當Spring處理用戶請求結束後,系統會自動觸發該事件;
1.簡單實現自定義監聽器,初步認識監聽器:結合上篇文章案例,實現監聽器監聽 Spring 容器變化
@Configuration public class MyApplicationListener implements ApplicationListener<ApplicationEvent> { //當容器發佈此事件以後,方法觸發 @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("當前收到的事件:"+event); } } =========測試運行結果========= [MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean的數量:11 十一月 03, 2020 10:17:59 下午 org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses 信息: Cannot enhance @Configuration bean definition 'myBeanDefinitionRegistryPostProcessor' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'. [MyBeanDefinitionRegistryPostProcessor]postProcessBeanFactory--->bean的數量:12 [MyBeanFactoryPostProcessor]調用了postProcessBeanFactory [MyBeanFactoryPostProcessor]當前beanFactory共有12個bean [MyBeanFactoryPostProcessor]當前beanFactory有下面組件[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, extConfig, myApplicationListener, myBeanDefinitionRegistryPostProcessor, myBeanFactoryPostProcessor, myBeanPostProcessor, person, color] PropertyValues: length=0 [MyBeanFactoryPostProcessor]postProcessBeanFactory方法中修改了name屬性初始值了 PropertyValues: length=1; bean property 'name' [MyBeanPostProcessor]後置處理器處理bean=【extConfig】開始 [MyBeanPostProcessor]後置處理器處理bean=【extConfig】完畢! [MyBeanPostProcessor]後置處理器處理bean=【myApplicationListener】開始 [MyBeanPostProcessor]後置處理器處理bean=【myApplicationListener】完畢! Person有參構造器:[name=張三,sex=男] [Person]調用了BeanNameAware的setBeanName方法了:person [Person]調用了BeanFactoryAware的setBeanFactory方法了:org.springframework.beans.factory.support.DefaultListableBeanFactory@e45f292: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,extConfig,myApplicationListener,myBeanDefinitionRegistryPostProcessor,myBeanFactoryPostProcessor,myBeanPostProcessor,person,color]; root of factory hierarchy [MyBeanPostProcessor]後置處理器處理bean=【person】開始 [Person]調用了Initailization的afterPropertiesSet方法了 [MyBeanPostProcessor]後置處理器處理bean=【person】完畢! [MyBeanPostProcessor]後置處理器處理bean=【color】開始 [MyBeanPostProcessor]後置處理器處理bean=【color】完畢! 當前收到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:17:59 CST 2020] Person [name=趙四, sex=null] 當前收到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:17:59 CST 2020] [Person]調用了DisposableBean的destroy方法了
2.自定義發佈一個事件,步驟以下:
1)實現一個監聽器來監聽某個事件(事件是實現了ApplicationEvent 及其子類的事件);
2)把監聽器加入到容器中;
2)發佈事件,只要容器有相關事件的發佈,咱們就能監聽到這個事件;
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class); Person bean = context.getBean(Person.class); System.out.println(bean.toString()); //發佈事件 context.publishEvent(new ApplicationEvent(new String("自定義事件")) { }); context.close(); } =========測試運行結果========= [MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean的數量:11 ............... [MyBeanPostProcessor]後置處理器處理bean=【color】完畢! 當前收到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:40:39 CST 2020] Person [name=趙四, sex=null] 當前收到的事件:com.hrh.ext.ExtTest$1[source=自定義事件] 當前收到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:40:39 CST 2020] [Person]調用了DisposableBean的destroy方法了
從上面的運行結果能夠看出,咱們在容器中添加了一個事件,當咱們發佈事件時,監聽器會監聽到事件並把事件的內容發佈出來。
3.自定義監聽器,實現容器的 bean都初始化後執行相應的操做,好比執行特定的方法:
1)自定義註解:
//該註解做用在類上 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface BaseService { }
2)兩個測試 Mapper:
@Configuration @BaseService public class TaskScheduleJobMapper { public void initMapper() { System.out.println(">>>>> 【initMapper】Start job <<<<<"); } } @Configuration @BaseService public class TaskScheduleJobTxlogMapper { public void initMapper() { System.out.println(">>>>> 【initMapper】Recording log <<<<<"); } }
3)測試系統入口:
public interface BaseInterface { public void init(); } @Configuration public class BaseInterfaceImpl implements BaseInterface { @Override public void init() { System.out.println("System start........"); } }
4)自定義監聽器:
@Configuration public class ApplicationContextListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { // root application context if (null == contextRefreshedEvent.getApplicationContext().getParent()) { System.out.println(">>>>> Spring初始化完畢 <<<<<"); // spring初始化完畢後,經過反射調用全部使用BaseService註解的initMapper方法 Map<String, Object> baseServices = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(BaseService.class); for (Object service : baseServices.values()) { System.out.println(">>>>> {" + service.getClass().getName() + "}.initMapper():"); try { Method initMapper = service.getClass().getMethod("initMapper"); initMapper.invoke(service); } catch (Exception e) { System.out.println("初始化BaseService的initMapper方法異常:" + e); e.printStackTrace(); } } // 系統入口初始化 Map<String, BaseInterface> baseInterfaceBeans = contextRefreshedEvent.getApplicationContext().getBeansOfType(BaseInterface.class); for (Object service : baseInterfaceBeans.values()) { System.out.println(">>>>> {" + service.getClass().getName() + "}.init()"); try { Method init = service.getClass().getMethod("init"); init.invoke(service); } catch (Exception e) { System.out.println("初始化BaseInterface的init方法異常:" + e); e.printStackTrace(); } } } } } =======測試運行結果======= [MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean的數量:14 ................ [MyBeanPostProcessor]後置處理器處理bean=【color】開始 [MyBeanPostProcessor]後置處理器處理bean=【color】完畢! >>>>> Spring初始化完畢 <<<<< >>>>> {com.hrh.ext.TaskScheduleJobMapper$$EnhancerBySpringCGLIB$$6bfe7114}.initMapper(): >>>>> 【initMapper】Start job <<<<< >>>>> {com.hrh.ext.TaskScheduleJobTxlogMapper$$EnhancerBySpringCGLIB$$7132ffe6}.initMapper(): >>>>> 【initMapper】Recording log <<<<< >>>>> {com.hrh.ext.BaseInterfaceImpl$$EnhancerBySpringCGLIB$$f49a26ba}.init() System start........ Person [name=趙四, sex=null] [Person]調用了DisposableBean的destroy方法了
4.自定義事件及監聽並進行發佈
1)自定義事件:
public class EmailEvent extends ApplicationEvent { private String address; private String text; /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ public EmailEvent(Object source) { super(source); } public EmailEvent(Object source, String address, String text) { super(source); this.address = address; this.text = text; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
2)自定義監聽器監聽自定義事件:
@Component public class EmailListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof EmailEvent) { EmailEvent emailEvent = (EmailEvent) event; System.out.println("郵件地址:" + emailEvent.getAddress()); System.out.println("郵件內容:" + emailEvent.getText()); } else { System.out.println("容器自己事件:" + event); } } }
3)測試發佈事件:
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class); Person bean = context.getBean(Person.class); System.out.println(bean.toString()); //建立一個ApplicationEvent對象 EmailEvent event = new EmailEvent("hello","249968839@qq.com","This is a event test"); //主動觸發該事件 context.publishEvent(event); context.close(); } =======測試運行結果======= [MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean的數量:15 ............. >>>>> Spring初始化完畢 <<<<< >>>>> {com.hrh.ext.TaskScheduleJobMapper$$EnhancerBySpringCGLIB$$45955a2d}.initMapper(): >>>>> 【initMapper】Start job <<<<< >>>>> {com.hrh.ext.TaskScheduleJobTxlogMapper$$EnhancerBySpringCGLIB$$4ac9e8ff}.initMapper(): >>>>> 【initMapper】Recording log <<<<< >>>>> {com.hrh.ext.BaseInterfaceImpl$$EnhancerBySpringCGLIB$$e5b13f13}.init() System start........ 容器自己事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Fri Nov 06 22:55:23 CST 2020] Person [name=趙四, sex=null] 容器自己事件:com.hrh.ext.ExtTest$1[source=自定義事件] 郵件地址:249968839@qq.com 郵件內容:This is a event test 容器自己事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Fri Nov 06 22:55:23 CST 2020] [Person]調用了DisposableBean的destroy方法了
上面說了幾個案例,如今你應該知道怎麼使用了吧,接下來經過 debug 代碼來分析它的執行流程。
1.先來看看 ContextRefreshedEvent事件是怎麼發佈的,仍是從熟悉的配方 refresh() 講起,當容器刷新後,能夠看到它調用了 finishRefresh() 來刷新執行事件:
public void refresh() throws BeansException, IllegalStateException { //來個鎖,否則 refresh() 還沒結束,你又來個啓動或銷燬容器的操做,那不就亂套了嘛 synchronized (this.startupShutdownMonitor) { .............. //初始化容器的信息源 initMessageSource(); //初始化事件監聽多路廣播器 initApplicationEventMulticaster(); //是個空殼方法,在AnnotationApplicationContex上下文中沒有實現,可能在spring後面的版本會去擴展。 onRefresh(); //註冊監聽器 registerListeners(); //對象的建立:初始化剩下全部的(非懶加載的)單實例對象 finishBeanFactoryInitialization(beanFactory); //刷新完成工做,包括初始化LifecycleProcessor,發佈刷新完成事件等 finishRefresh(); } ................. }
2.在 AbstractApplicationContext.finishRefresh 方法中就會發布(廣播)一條表明初始化結束的消息:publishEvent(new ContextRefreshedEvent(this))
protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). clearResourceCaches(); // Initialize lifecycle processor for this context. initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); //發佈(廣播)一條消息,類型ContextRefreshedEvent表明Spring容器初始化結束 publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }
3.繼續 publishEvent 方法會發現執行了getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) 來廣播消息:
public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event;//類型轉換 } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // Multicast right now if possible - or lazily once the multicaster is initialized //初始化過程當中的registerListeners方法會把earlyApplicationEvents設置爲空,(早期事件,容器初始化時使用,能夠忽略) if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { //執行廣播消息 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... 方便使用父類進行發佈事件 if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
PS:publishEvent 方法是發佈(廣播)服務的核心能力,而它定義在 ApplicationEventPublisher 接口中,ApplicationContext接口繼承了 ApplicationEventPublisher,因此 AbstractApplicationContext抽象類(ApplicationContext接口的實現類)就實現了該方法,也具備了發送廣播的能力。
上面的 getApplicationEventMulticaster() 是什麼東西呢?須要深刻了解下,它的做用是獲取事件的多播器(派發器),即將事件發送給多個監聽器,讓監聽器執行相應的邏輯。
private ApplicationEventMulticaster applicationEventMulticaster; ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException { if (this.applicationEventMulticaster == null) { throw new IllegalStateException("ApplicationEventMulticaster not initialized - " + "call 'refresh' before multicasting events via the context: " + this); } return this.applicationEventMulticaster; }
從上面的代碼能夠看出,applicationEventMulticaster是 AbstractApplicationContext 的私有成員變量,那麼這個多播器(派發器)是怎麼獲取到的呢?在前面的 refresh 方法中有一個initApplicationEventMulticaster()方法,就是調用 AbstractApplicationContext.initApplicationEventMulticaster() 來初始化這個多播器(派發器)的:
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); //從bean工廠查找有沒有一個bean爲applicationEventMulticaster,若是有,從容器中拿出來 if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isTraceEnabled()) { logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } else { //若是沒有,則往容器中註冊一個SimpleApplicationEventMulticaster,名字爲applicationEventMulticaster,若是派發事件須要就可使用了 this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isTraceEnabled()) { logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " + "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); } } }
4.說完了 getApplicationEventMulticaster(),再來講說 multicastEvent(),它的做用是派發事件,它是 ApplicationEventMulticaster接口的一個方法,因此它會調用實現類 SimpleApplicationEventMul
ticaster(上面註冊的多播器)的multicastEvent 方法:
public interface ApplicationEventMulticaster { void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); } //public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); //根據消息類型取出對應的全部監聽器 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { //若是可使用多線程執行,就使用多線程來異步派發事件,執行監聽器的方法 if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } }
5.最後來看看 invokeListener 方法,它會拿到監聽器來回調 MyApplicationListener. onApplicationEvent方法,而後控制檯就輸出了信息 :
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event);//執行 } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { //回調onApplicationEvent listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
6.當再執行案例2的事件時,從AbstractApplicationContext. publishEvent()(上面第3步)開始執行,步驟仍是multicastEvent派發事件 --> invokeListener回調執行 onApplicationEvent;
7.當容器關閉時,會調用doClose(),裏面有個ContextClosedEvent事件,監聽器監聽到事件控制檯就輸出了信息:
context.close(); @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } protected void doClose() { // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isDebugEnabled()) { logger.debug("Closing " + this); } LiveBeansView.unregisterApplicationContext(this); try { // Publish shutdown event. publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); } // Stop all Lifecycle beans, to avoid delays during individual destruction. if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn("Exception thrown from LifecycleProcessor on context close", ex); } } // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // Close the state of this context itself. closeBeanFactory(); // Let subclasses do some final clean-up if they wish... onClose(); // Reset local application listeners to pre-refresh state. if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Switch to inactive. this.active.set(false); } }
8.上面在第4步中派發事件操做 getApplicationListeners 拿到了全部的監聽器,那麼容器中有哪些監聽器呢?從第1步的 registerListeners() 能夠看到,容器是先註冊了多播器和監聽器後才進行事件的發佈,下面是監聽器的註冊:
protected void registerListeners() { // Register statically specified listeners first. //從容器中拿到全部的監聽器添加到多路派發器中,(最開始是沒有的,跳過循環執行下面的步驟) for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let post-processors apply to them! //若容器中沒有,即上面的遍歷沒有執行,則根據類型獲取組件名稱,而後根據組件名稱獲取對應的組件加入到多路派發器中 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // Publish early application events now that we finally have a multicaster... Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
下面再來看看 getApplicationListeners 方法,發現它是 AbstractApplicationEventMulticaster 類的一個方法,因此在開始詳解 getApplicationListeners 方法前,先來看看 AbstractApplicationEventMulticaster 類是一個起什麼做用的類。
從上面類圖能夠看到 AbstractApplicationEventMulticaster 它實現了幾個接口,它是一個事件派發器,上文第3點裏面講到的初始化派發器獲取的 SimpleApplicationEventMulticaster 實際上是繼承自AbstractApplicationEventMulticaster 的。
在看下面的獲取監聽器源碼前,咱們先思考一個問題:監聽器如何作到只監聽指定類型的消息?如何要實現,有什麼方式?
咱們能夠先猜想下:
1) 註冊監聽器的時候,將監聽器和消息類型綁定;(該論證可查看下文 addApplicationListener 實現方法的解析)
2) 廣播的時候,按照這條消息的類型去找指定了該類型的監聽器,但不可能每條廣播都去全部監聽器裏面找一遍,應該是說廣播的時候會觸發一次監聽器和消息的類型綁定;
好了,接下來咱們帶着這些問題和猜想詳細看看 getApplicationListeners 的源碼:
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware { //建立監聽器助手類,用於存放監聽器集合,參數是不是預過濾監聽器 private final ListenerRetriever defaultRetriever = new ListenerRetriever(false); //ListenerCacheKey是基於事件類型和源類型的類做爲key用來存儲監聽器助手 final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64); @Nullable private ClassLoader beanClassLoader;//類加載器 //互斥的監聽器助手類 private Object retrievalMutex = this.defaultRetriever; //監聽器助手類(封裝一組特定目標監聽器的幫助類,容許有效地檢索預過濾的監聽器,此助手的實例按照時間類型和源類型緩存) private class ListenerRetriever { //存放事件監聽器,有序、不可重複 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>(); //存放事件監聽器bean名稱,有序,不可重複 public final Set<String> applicationListenerBeans = new LinkedHashSet<>(); //是否預過濾監聽器 private final boolean preFiltered; public ListenerRetriever(boolean preFiltered) { this.preFiltered = preFiltered; } //獲取事件監聽器 public Collection<ApplicationListener<?>> getApplicationListeners() { //建立一個指定大小的ApplicationListener監聽器List集合 List<ApplicationListener<?>> allListeners = new ArrayList<>( this.applicationListeners.size() + this.applicationListenerBeans.size()); allListeners.addAll(this.applicationListeners); //若是存放監聽器bean name的集合不爲空 if (!this.applicationListenerBeans.isEmpty()) { //獲取IOC容器工廠類 BeanFactory beanFactory = getBeanFactory(); for (String listenerBeanName : this.applicationListenerBeans) { try { //獲取指定bean name的監聽器實例 ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); //斷定若是是預過濾的監聽器或者集合中不包含監聽器實例則添加到集合中 if (this.preFiltered || !allListeners.contains(listener)) { allListeners.add(listener); } } catch (NoSuchBeanDefinitionException ex) { // Singleton listener instance (without backing bean definition) disappeared - // probably in the middle of the destruction phase } } } if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) { AnnotationAwareOrderComparator.sort(allListeners); } return allListeners; } } //流程1:當所發佈的事件類型和事件源類型與Map(retrieverCache)中的key匹配時, //將直接返回value中的監聽器列表做爲匹配結果,一般這發生在事件不是第一次發佈時,能避免遍歷全部監聽器並進行過濾, //若是事件時第一次發佈,則會執行流程2。 protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { //事件源,事件最初發生在其上的對象 Object source = event.getSource(); //事件源class對象 Class<?> sourceType = (source != null ? source.getClass() : null); //緩存的key有兩個維度:消息來源+消息類型(關於消息來源可見ApplicationEvent構造方法的入參) //建立基於事件源和源類型的監聽器助手cacheKey ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // Quick check for existing entry on ConcurrentHashMap... // retrieverCache是ConcurrentHashMap對象(併發容器,使用鎖分段來確保多線程下數據安全),因此是線程安全的, // ListenerRetriever中有個監聽器的集合,並有些簡單的邏輯封裝,調用它的getApplicationListeners方法返回的監聽類集合是排好序的(order註解排序) ListenerRetriever retriever = this.retrieverCache.get(cacheKey); //若是retrieverCache中找到對應的監聽器集合,就當即返回了 if (retriever != null) { return retriever.getApplicationListeners(); } //若是類加載器爲null,或者事件源在給定的類加載器上下文是安全的而且源類型爲null或者源類型在指定上下文是安全的 if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { // Fully synchronized building and caching of a ListenerRetriever //鎖定監聽器助手對象,同步從ListenerRetriever監聽器助手中獲取指定的監聽器 synchronized (this.retrievalMutex) { //搶到鎖以後再作一次判斷,由於有可能在前面BLOCK的時候,另外一個搶到鎖的線程已經設置好了緩存, //即避免本身在BLOCK的時候其餘線程已經將數據放入緩存了 retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { //返回監聽器助手中存儲的監聽器對象 return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); //retrieveApplicationListeners方法實際檢索給定事件和源類型的監聽器 Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);//流程2 //retriever放到緩存器中(更新緩存) this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { // No ListenerRetriever caching -> no synchronization necessary // 無ListenerRetriever監聽器助手 -> 無需同步緩存 return retrieveApplicationListeners(eventType, sourceType, null); } } }
從上面的源碼能夠看出:
1)在獲取 ApplicationListener 的時候用到了緩存,同時有緩存更新和用鎖來確保線程同步(雙重判斷也作了),這樣若是在自定義廣播時,若是多線程同時發廣播,就不會有線程同步的問題了。
2)發送消息的時候根據類型去找全部對應的監聽器,這樣就能夠實現自定義監聽器只接收指定類型的消息。
3)在廣播消息的時刻,若是某個類型的消息在緩存中找不到對應的監聽器集合,就調用 retrieveApplicationListeners 方法去找出符合條件的全部監聽器,而後放入這個集合中。
下面再詳細看看 retrieveApplicationListeners:
private Collection<ApplicationListener<?>> retrieveApplicationListeners( ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { //存放匹配的監聽器的列表 List<ApplicationListener<?>> allListeners = new ArrayList<>(); Set<ApplicationListener<?>> listeners; Set<String> listenerBeans; //鎖定監聽器助手對象 synchronized (this.retrievalMutex) { //獲取監聽器助手中存儲的監聽器對象,去重 listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); //獲取監聽器助手中存儲的監聽器bean名稱集合,去重 listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); } // Add programmatically registered listeners, including ones coming // from ApplicationListenerDetector (singleton beans and inner beans). ////遍歷全部的監聽器 for (ApplicationListener<?> listener : listeners) { //判斷監聽器是否匹配的邏輯在supportsEvent(listener, eventType, sourceType)中 if (supportsEvent(listener, eventType, sourceType)) { //監聽器助手類不爲空,加入監聽器助手類中的監聽器集合中 if (retriever != null) { retriever.applicationListeners.add(listener); } //放入匹配的監聽器列表中 allListeners.add(listener); } } // Add listeners by bean name, potentially overlapping with programmatically // registered listeners above - but here potentially with additional metadata. //監聽器助手中存儲的監聽器bean名稱集合有值 if (!listenerBeans.isEmpty()) { //獲取IOC容器 ConfigurableBeanFactory beanFactory = getBeanFactory(); //遍歷 for (String listenerBeanName : listenerBeans) { try { //判斷監聽器是否匹配的邏輯在supportsEvent(listener, eventType, sourceType)中 if (supportsEvent(beanFactory, listenerBeanName, eventType)) { //獲取指定bean name的監聽器實例 ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); //若是匹配的監聽器列表不包含上面獲取的實例和符合supportsEvent的邏輯 if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { //監聽器助手類不爲空 if (retriever != null) { //bean名稱是單例,在容器中沒有重複監聽器 if (beanFactory.isSingleton(listenerBeanName)) { //加入監聽器助手類中的監聽器集合中 retriever.applicationListeners.add(listener); } else { //去重,applicationListenerBeans是LinkedHashSet retriever.applicationListenerBeans.add(listenerBeanName); } } //放入匹配的監聽器列表中 allListeners.add(listener); } } //監聽器是否匹配的邏輯不在supportsEvent(listener, eventType, sourceType)中,從監聽器助手類和匹配的監聽器列表中移除 else { // Remove non-matching listeners that originally came from // ApplicationListenerDetector, possibly ruled out by additional // BeanDefinition metadata (e.g. factory method generics) above. Object listener = beanFactory.getSingleton(listenerBeanName); if (retriever != null) { retriever.applicationListeners.remove(listener); } allListeners.remove(listener); } } catch (NoSuchBeanDefinitionException ex) { // Singleton listener instance (without backing bean definition) disappeared - // probably in the middle of the destruction phase } } } //監聽器進行排序 AnnotationAwareOrderComparator.sort(allListeners); if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { retriever.applicationListeners.clear(); retriever.applicationListeners.addAll(allListeners); } return allListeners; }
下面是 supportsEvent 的源碼探究:
//首先對原始的ApplicationListener進行一層適配器包裝成GenericApplicationListener, //便於後面使用該接口中定義的方法判斷監聽器是否支持傳入的事件類型或事件源類型 protected boolean supportsEvent( ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); } public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered { //判斷是否支持該事件類型 boolean supportsEventType(ResolvableType eventType); //判斷是否支持該事件源類型,默認是true,也就是說事件源的類型一般對於判斷匹配的監聽器沒有意義 default boolean supportsSourceType(@Nullable Class<?> sourceType) { return true; } } public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener { ...... //監聽器泛型的實際類型 @Nullable private final ResolvableType declaredEventType; //判斷監聽器是否支持該事件類型,由於咱們的監聽器實例一般都不是SmartApplicationListener類型 //eventType是發佈的事件的類型 @Override @SuppressWarnings("unchecked") public boolean supportsEventType(ResolvableType eventType) { if (this.delegate instanceof SmartApplicationListener) { Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve(); return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass)); } else { //this.declaredEventType.isAssignableFrom(eventType)當如下兩種狀況返回true // 1.declaredEventType和eventType類型相同 // 2.declaredEventType是eventType的父類型 //只要監聽器泛型的實際類型和發佈的事件類型同樣或是它的父類型,則該監聽器將被成功匹配。 return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType)); } } }
9.下面是流程總結圖:
1.從上面的案例中能夠看到,只要 bean 繼承 ApplicationEvent,而後使用容器的 publishEvent 就能夠發佈事件了(相似上文案例4),那麼還有其餘的方式使 bean 具備跟容器同樣具備發佈事件的能力嗎?
答案固然是有的,好比 ApplicationEventPublisherAware 這個接口就可使 bean 具備發佈事件的能力。下面是該接口的源碼:
public interface ApplicationEventPublisherAware extends Aware { void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher); }
咱們能夠建立一個bean,實現了 ApplicationEventPublisherAware 接口,那麼該 bean 的 setApplicationEventPublisher 方法就會被調用,經過該方法能夠接收到 ApplicationEventPublisher 類型的入參,藉助這個 ApplicationEventPublisher 就能夠發消息了;
好比下面的案例:
1)接口:
public interface UserEventRegisterService { /** * 發佈事件,註冊用戶 */ void register(); }
2)接口實現:
@Service public class UserEventRegisterServiceImpl implements UserEventRegisterService { @Resource private ApplicationEventPublisher applicationEventPublisher; @Override public void register() { User user = new User(); user.setId(1); user.setName("張三"); user.setSex("男"); applicationEventPublisher.publishEvent(user); System.out.println("結束。"); } }
3)自定義監聽器:可使用 @EventListener 註解來實現自定義監聽器(該註解下文詳細介紹)
@Component public class UserEventListener { @EventListener(condition = "#user.id != null")//監聽當用戶id不爲空的事件 public void handleEvent(User user){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("監聽器監聽到的信息:"+user); } }
4)配置類:
@ComponentScan("com.hrh.ext") @Configuration public class ExtConfig { }
5)測試:
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class); UserEventRegisterService service = context.getBean(UserEventRegisterService.class); service.register(); context.close(); } ======運行結果====== 監聽器監聽到的信息:User [id=1, name=張三, sex=男] 結束。
2.監聽器是如何加入到容器中的呢?咱們從容器 refresh 刷新開始看起,發如今 refresh 裏面有 prepareBeanFactory(beanFactory) 方法,爲全部bean準備了一個後置處理器 ApplicationListenerDetector:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); ......... } } protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { .......... // Register early post-processor for detecting inner beans as ApplicationListeners. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); .......... }
接下來再看看 ApplicationListenerDetector類的源碼,因爲它是一個後置處理器(不清楚後置處理器的可查看上篇文章,開頭有簡單介紹),因此它有 postProcessBeforeInitialization 和 postProcessAfterInitialization 兩個方法,這兩個方法在對全部的 bean 進行實例化後進行攔截操做:
private final transient AbstractApplicationContext applicationContext; private final transient Map<String, Boolean> singletonNames = new ConcurrentHashMap<>(256); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { //若是bean是ApplicationListener類型 if (bean instanceof ApplicationListener) { // potentially not detected as a listener by getBeanNamesForType retrieval //判斷bean在不在併發容器中 Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { // singleton bean (top-level or inner): register on the fly //註冊監聽器,其實就是保存在成員變量applicationEventMulticaster的成員變量defaultRetriever的集合applicationListeners中 this.applicationContext.addApplicationListener((ApplicationListener<?>) bean); } else if (Boolean.FALSE.equals(flag)) { if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) { // inner bean with other scope - can't reliably process events logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " + "but is not reachable for event multicasting by its containing ApplicationContext " + "because it does not have singleton scope. Only top-level listener beans are allowed " + "to be of non-singleton scope."); } this.singletonNames.remove(beanName); } } return bean; } public void addApplicationListener(ApplicationListener<?> listener) { Assert.notNull(listener, "ApplicationListener must not be null"); if (this.applicationEventMulticaster != null) { this.applicationEventMulticaster.addApplicationListener(listener); } this.applicationListeners.add(listener); }
如上所示,若是當前 bean 實現了 ApplicationListener 接口,就會調用 this.applicationContext.addApplicationListener 方法將當前 bean 註冊到 applicationContext 的監聽器集合中,後面有廣播就直接找到這些監聽器,調用每一個監聽器的 onApplicationEvent 方法;
接下來詳細看看 addApplicationListener 方法,它的實現方法在 AbstractApplicationEventMulticaster 類中:
@Override public void addApplicationListener(ApplicationListener<?> listener) { //鎖定監聽器助手對象 synchronized (this.retrievalMutex) { // Explicitly remove target for a proxy, if registered already, // in order to avoid double invocations of the same listener. // 若是已經註冊,則顯式刪除已經註冊的監聽器對象 // 爲了不調用重複的監聽器對象 Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); if (singletonTarget instanceof ApplicationListener) { //若是由於AOP致使建立了監聽類的代理,那麼就要在註冊列表中清除代理類 this.defaultRetriever.applicationListeners.remove(singletonTarget); } //把監聽器加入集合defaultRetriever.applicationListeners中,這是個LinkedHashSet實例 this.defaultRetriever.applicationListeners.add(listener); //清空監聽器助手緩存Map this.retrieverCache.clear(); } }
在上面能夠看到,若是對象是由 AOP生成的代理類,須要清除,是由於 AOP 是經過代理技術實現的,此時可能經過 CGLIB 生成了監聽類的代理類,此類的實例若是被註冊到監聽器集合中,那麼廣播時按照消息類型就會取出兩個監聽器實例來,到時就是一個消息被兩個實例消費了,所以須要先清理掉代理類。
同時也論證了一個觀點:所謂的註冊監聽器,其實就是把 ApplicationListener 的實現類放入一個LinkedHashSet的集合,此處沒有任何與消息類型相關的操做,所以,監聽器註冊的時候並無將消息類型和監聽器綁定;
3.異步監聽事件案例:正常的事件通知是 ContextRefreshedEvent --> EmailEvent --> ContextClosedEvent
//開啓異步支持 @EnableAsync @Component public class EmailListener implements ApplicationListener { @Async//異步執行 @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof EmailEvent) { EmailEvent emailEvent = (EmailEvent) event; System.out.println("郵件地址:" + emailEvent.getAddress()); System.out.println("郵件內容:" + emailEvent.getText()); } else { System.out.println("容器自己事件:" + event); } //經過線程名稱區別 System.out.println(event+ ":" + Thread.currentThread().getName()); } } =======測試運行結果:從下面的運行結果能夠看出是異步執行了======= 郵件地址:249968839@qq.com 郵件內容:This is a event test com.hrh.ext.EmailEvent[source=hello]:SimpleAsyncTaskExecutor-2 容器自己事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020] 容器自己事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020] org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]:SimpleAsyncTaskExecutor-1 org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]:SimpleAsyncTaskExecutor-3
5.Spring事務監聽機制---使用 @TransactionalEventListener 處理數據庫事務提交成功後再執行操做
在項目中,每每須要執行數據庫操做後,發送消息或事件來異步調用其餘組件執行相應的操做,例如:用戶註冊後發送激活碼、配置修改後發送更新事件等。
可是,數據庫的操做若是還未完成,此時異步調用的方法查詢數據庫發現沒有數據,這就會出現問題。
以下面僞代碼案例:
void saveUser(User u) { //保存用戶信息 userDao.save(u); //觸發保存用戶事件 applicationContext.publishEvent(new SaveUserEvent(u.getId())); } @EventListener void onSaveUserEvent(SaveUserEvent event) { //獲取事件中的信息(用戶id) Integer id = event.getEventData(); //查詢數據庫,獲取用戶(此時若是用戶還未插入數據庫,則返回空) User u = userDao.getUserById(id); //這裏可能報空指針異常! String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); }
爲了解決上述問題,Spring爲咱們提供了兩種方式: @TransactionalEventListener 註解 和 事務同步管理器 TransactionSynchronizationManager,以便咱們能夠在事務提交後再觸發某一事件。
@TransactionalEventListener 註解的使用案例:只有當前事務提交以後,纔會執行事件監聽器的方法
//phase默認爲AFTER_COMMIT,共有四個枚舉:BEFORE_COMMIT,AFTER_COMMIT,AFTER_ROLLBACK,AFTER_COMPLETION
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) void onSaveUserEvent(SaveUserEvent event) { Integer id = event.getEventData(); User u = userDao.getUserById(id); String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); }
TransactionSynchronizationManager 的使用案例:@TransactionalEventListener底層下面這樣來實現的
@EventListener void onSaveUserEvent(SaveUserEvent event) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { Integer id = event.getEventData(); User u = userDao.getUserById(id); String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); } }); }