Spring中ApplicationEvent事件機制源碼詳解
一、ApplicationEvent應用
先定義一個 Event : ScorpiosEvent ,繼承 ApplicationEvent
public class ScorpiosEvent extends ApplicationEvent { private String msg; public ScorpiosEvent(Object source,String message) { super(source); this.msg = message; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
定義兩個 Listener : ScorpiosListener 、 TestListener 。要加上 @Componen t注解,讓 Spring 容器管理。
// ScorpiosListener @Component public class ScorpiosListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if(event instanceof ScorpiosEvent){ System.out.println("ScorpiosEvent ..." + ((ScorpiosEvent) event).getMsg()); }else{ System.out.println("ScorpiosEvent....dododododododododo"); } } }
// TestListener @Component public class TestListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if(event instanceof ScorpiosEvent){ System.out.println("TestListener ..." + ((ScorpiosEvent) event).getMsg()); }else{ System.out.println("TestListener....tttttttttttttttttttttt"); } } }
入口函數(shù)
public static void main( String[] args ) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(AppConfig.class); ac.refresh(); ScorpiosEvent scorpiosEvent = new ScorpiosEvent("scorpios","aaaaaaaaaa"); ac.publishEvent(scorpiosEvent); }
上面就是 Event 的具體的一個應用, Listener 要被 Spring 容器管理。
從上面的代碼調(diào)用和日志可以看到,只要調(diào)用一個發(fā)布事件 ac.publishEvent(scorpiosEvent) ,所有的 Listener 都會被調(diào)用。注意,是所有的Listener,后面源碼分析。
從上面的輸出日志中可以看出, ScorpiosListener 、 TestListener 監(jiān)聽器都被調(diào)用了兩次,可代碼里,明明就調(diào)用了一次啊,為什么 System.out.println("ScorpiosEvent....dododododododododo") 這行代碼也會被輸出呢?這是為什么呢?下面就來分析下 Spring 事件機制的源碼吧。
二、ApplicationEvent源碼分析
Spring 的事件機制采用的是觀察者設(shè)計模式
1. Listener監(jiān)聽器的注冊過程
下面首先來了解下 Listener 監(jiān)聽器是何時被添加到 Spring 容器中的, Listener 被掃描后具體是存放在哪里的?下面先看一下 Spring 中大名鼎鼎的 refresh() 方法,如果對這個方法不了解,可以看下我對 Spring 源碼解析的其他文章。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 準備工作,包括設(shè)置啟動時間,是否激活標識位,初始化屬性源(property source)配置 prepareRefresh(); // 返回一個factory 為什么需要返回一個工廠? 因為要對工廠進行初始化 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 準備工廠 prepareBeanFactory(beanFactory); try { // 這個方法在當前版本的spring是沒用任何代碼的,可能spring期待在后面的版本中去擴展吧 postProcessBeanFactory(beanFactory); // 調(diào)用BeanFactoryPostProcessors的后置處理器 // 在Spring環(huán)境中去執(zhí)行已經(jīng)被注冊的FactoryProcessors // 設(shè)置執(zhí)行自定義的ProcessorBeanFactory和Spring內(nèi)部自己定義的 invokeBeanFactoryPostProcessors(beanFactory); //-------------------到此spring工廠完成創(chuàng)建工作-------------------------- // 注冊BeanPostProcessor后置處理器 registerBeanPostProcessors(beanFactory); initMessageSource(); // 初始化應用事件廣播器 initApplicationEventMulticaster(); onRefresh(); // 注冊監(jiān)聽器 registerListeners(); // 實例化單實例非懶加載的Bean finishBeanFactoryInitialization(beanFactory); // 此處會發(fā)布一個事件:ContextRefreshedEvent finishRefresh(); } } }
主要看下面這四個方法:
- initApplicationEventMulticaster()
- registerListeners()
- finishBeanFactoryInitialization(beanFactory)
- finishRefresh()
注意:在上面這四個方法執(zhí)行之前,Spring的組件掃描工作已經(jīng)結(jié)束了,但Bean實例化還沒有。
2. initApplicationEventMulticaster()
此方法的作用是初始化應用事件廣播器,這個廣播器是干嘛的呢?說的直白點,里面存放了所有的 Listener
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 工廠里是否包含 “applicationEventMulticaster” Bean if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); } else { // 沒有的話,Spring自己創(chuàng)建一個SimpleApplicationEventMulticaster this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); // 將自己創(chuàng)建的SimpleApplicationEventMulticaster放到Spring容器中, // name 為:applicationEventMulticaster beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); } } }
此處是Spring自己創(chuàng)建了一個 SimpleApplicationEventMulticaster ,放入到 Spring 容器中,看斷點圖。
3. registerListeners()
此方法里面是從 Spring 容器中,拿到實現(xiàn)了 ApplicationListener 接口的所有 BeanName ,然后把它們名字添加到廣播器中的 this.defaultRetriever.applicationListenerBeans 中
protected void registerListeners() { // 注冊靜態(tài)指定的監(jiān)聽器,沒有 for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // 從Spring容器中拿到所有實現(xiàn)了ApplicationListener接口的類,此處能拿到我們添加的兩個Listener String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { // 獲取上面創(chuàng)建的ApplicationEventMulticaster廣播器,把listenerBeanName放到廣播器中的 // this.defaultRetriever.applicationListenerBeans這個集合中,注意此處是放的是listenerBeanName getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
4. finishBeanFactoryInitialization()
此方法就是實例化單例非懶加載的 Bean 。這個方法就不具體介紹了, Spring 源碼分析的其他文章中有詳細介紹。這地方給個斷點圖吧,看看執(zhí)行到此方法時, Spring 容器中有哪些 Bean 。
從上面的斷點圖中可以看出, ScorpiosListener 、 TestListener 兩個 Listener 已經(jīng)被掃描到,但還沒有被實例化,所以下面會進行它們的實例化操作。那么這兩個Listener是怎么被保存到廣播器 ApplicationEventMulticaster 中的呢?答案是通過 ApplicationListenerDetector 這個 BeanPostProcessor 后置處理器。
// ApplicationListenerDetector類 public Object postProcessAfterInitialization(Object bean, String beanName) { // 判斷當前的Bean是不是實現(xiàn)了ApplicationListener接口 // 這兩個ScorpiosListener、TestListener肯定是的啊 // 此處的Bean已經(jīng)實例化好了 if (bean instanceof ApplicationListener) { Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { // 將這個實現(xiàn)了ApplicationListener接口的Bean放到廣播器的 // this.defaultRetriever.applicationListeners屬性中 this.applicationContext.addApplicationListener((ApplicationListener<?>) bean); } else if (Boolean.FALSE.equals(flag)) { this.singletonNames.remove(beanName); } } return bean; }
// AbstractApplicationContext類中方法 public void addApplicationListener(ApplicationListener<?> listener) { if (this.applicationEventMulticaster != null) { this.applicationEventMulticaster.addApplicationListener(listener); } this.applicationListeners.add(listener); } // AbstractApplicationEventMulticaster類中方法 public void addApplicationListener(ApplicationListener<?> listener) { synchronized (this.retrievalMutex) { Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); if (singletonTarget instanceof ApplicationListener) { this.defaultRetriever.applicationListeners.remove(singletonTarget); } // 將listener放到廣播器的屬性中了?。。。?! this.defaultRetriever.applicationListeners.add(listener); this.retrieverCache.clear(); } }
上面是 Listener 實例化和最終保存在哪里的源碼分析,下面要看一下,發(fā)布事件是怎么觸發(fā)監(jiān)聽器的調(diào)用的呢?
5. finishRefresh()
在此方法中, Spring 發(fā)布了一個事件: ContextRefreshedEvent 。
protected void finishRefresh() { clearResourceCaches(); initLifecycleProcessor(); getLifecycleProcessor().onRefresh(); // 發(fā)布事件,就關(guān)注這一個方法!?。?! publishEvent(new ContextRefreshedEvent(this)); LiveBeansView.registerApplicationContext(this); }
下面來看一下這個 publishEvent() 方法
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { ApplicationEvent applicationEvent; // 判斷這個event是不是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 { // 拿到廣播器,然后調(diào)用廣播器的multicastEvent()方法 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
AbstractApplicationContext 類中的 getApplicationEventMulticaster() 方法
// 此方法拿到之前創(chuàng)建的廣播器applicationEventMulticaster // 還記得是什么類型的嘛?對,是它:SimpleApplicationEventMulticaster ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException { if (this.applicationEventMulticaster == null) { // 拋異常代碼略 } return this.applicationEventMulticaster; }
SimpleApplicationEventMulticaster 廣播器中的 multicastEvent() 方法
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); // 此getApplicationListeners(event, type)方法就是拿到廣播器里面的所有Listener for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { // 調(diào)用監(jiān)聽器的方法?。。?! invokeListener(listener, event); } } } protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { doInvokeListener(listener, event); } else { // 參數(shù)傳入的是listener,終于開始調(diào)用了?。。?! doInvokeListener(listener, event); } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { // 調(diào)用Listener中僅有的onApplicationEvent()方法?。?! listener.onApplicationEvent(event); } catch (ClassCastException ex) { // 拋異常代碼略 } }
終于分析完了。這里是 Spring 自己發(fā)布的一個 Event ,所以會走到 ScorpiosListener 、 TestListener 兩個監(jiān)聽器里面的else代碼,打印 System.out.println("ScorpiosEvent....dododododododododo") , System.out.println("TestListener....tttttttttttttttttttttt") 這兩行代碼。
當執(zhí)行到 ac.publishEvent(scorpiosEvent) 這行代碼發(fā)布 Event 事件時,廣播器又會去調(diào)用所有的 Listener ,所以會有這兩行代 System.out.println("ScorpiosEvent ..." + ((ScorpiosEvent) event).getMsg()) 碼的輸出!??!
三、 小結(jié)
Spring 中事件機制使用的是觀察者設(shè)計模式,其中對應觀察者的四個角色分別為:
- 事件Event: ApplicationEvent 是所有事件對象的父類。 ApplicationEvent 繼承自 JDK 中的 EventObject ,所有的事件都需要繼承 ApplicationEvent ,并且通過 source 得到事件源
Spring 也為我們提供了很多內(nèi)置事件, ContextRefreshedEvent 、 ContextStartedEvent 、 ContextStoppedEvent 、 ContextClosedEvent 、 RequestHandledEvent 。
- 事件監(jiān)聽器: ApplicationListener ,也就是觀察者,繼承自 JDK 的 EventListener ,該類中只有一個方法 onApplicationEvent() ,當監(jiān)聽的事件發(fā)生后該方法會被執(zhí)行
- 事件源: ApplicationContext , ApplicationContext 是 Spring 中的核心容器,在事件監(jiān)聽中 ApplicationContext 可以作為事件的發(fā)布者,也就是事件源。因為 ApplicationContext繼承自 ApplicationEventPublisher。在 ApplicationEventPublisher中定義了事件發(fā)布的方法:publishEvent(Object event)
- 事件管理:ApplicationEventMulticaster,用于事件監(jiān)聽器的注冊和事件的廣播。監(jiān)聽器的注冊就是通過它來實現(xiàn)的,它的作用是把 Applicationcontext發(fā)布的 Event廣播給它的監(jiān)聽器列表。 因為它里面手握所有監(jiān)聽器。
Spring中的事件模型是一種簡單的、粗粒度的監(jiān)聽模型,當有一個事件到達時,所有的監(jiān)聽器都會接收到,并且作出響應,如果希望只針對某些類型進行監(jiān)聽,需要在代碼中進行控制。
當ApplicationContext接收到事件后,事件的廣播是Spring內(nèi)部給我們做的,其實在Spring讀包掃描之后,將所有實現(xiàn)ApplicationListener的Bean找出來,注冊為容器的事件監(jiān)聽器。當接收到事件的 時候,Spring會逐個調(diào)用事件監(jiān)聽器。
到此這篇關(guān)于Spring中ApplicationEvent事件機制源碼詳解的文章就介紹到這了,更多相關(guān)ApplicationEvent事件機制源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring中ApplicationEventPublisher發(fā)布訂閱模式的實現(xiàn)
- SpringBoot使用ApplicationEvent&Listener完成業(yè)務解耦
- 使用Spring的ApplicationEvent實現(xiàn)本地事件驅(qū)動的實現(xiàn)方法
- SpringBoot中ApplicationEvent和ApplicationListener用法小結(jié)
- 基于Spring Boot應用ApplicationEvent案例場景
- 詳解SpringBoot實現(xiàn)ApplicationEvent事件的監(jiān)聽與發(fā)布
- 詳解SpringBoot 發(fā)布ApplicationEventPublisher和監(jiān)聽ApplicationEvent事件
相關(guān)文章
關(guān)于mybatis mapper類注入失敗的解決方案
這篇文章主要介紹了關(guān)于mybatis mapper類注入失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04Spring boot如何基于攔截器實現(xiàn)訪問權(quán)限限制
這篇文章主要介紹了Spring boot如何基于攔截器實現(xiàn)訪問權(quán)限限制,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10java為什么使用BlockingQueue解決競態(tài)條件問題面試精講
這篇文章主要為大家介紹了java為什么使用BlockingQueue解決競態(tài)條件問題面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10SpringBoot+Hibernate實現(xiàn)自定義數(shù)據(jù)驗證及異常處理
這篇文章主要為大家介紹了SpringBoot如何整合Hibernate自定義數(shù)據(jù)驗證及多種方式異常處理,文中的示例代碼講解詳細,感興趣的可以了解一下2022-04-04