Spring的context包是對於beans包的功能擴展,比如在BeanFactory的基礎容器之上擴展為了ApplicationContext上下文。而ApplicationContext處理包含了BeanFactory的全部基礎功能之外,還額外提供了大量的擴展功能,本文主要分析下Spring提供的事件監聽機制,這里就使用到了設計模式中的觀察者設計模式。話不多說,直接正文。
一、Spring事件監聽機制的定義
使用過MQ的或者了解觀察者設計模式的同學應該大致都了解,實現事件監聽機制至少四個核心:事件、事件生產者和事件消費者,另外還需要有一個管理生產者、消費者和事件之間的注冊監聽關系的控制器。
在Spring中,事件監聽機制主要實現是通過事件、事件監聽器、事件發布者和事件廣播器來實現
1.1、Spring中的事件(ApplicationEvent)
spring中的事件有一個抽象父類ApplicationEvent,該類包含有當前ApplicationContext的引用,這樣就可以確認每個事件是從哪一個Spring容器中發生的。
1.2、Spring中的事件監聽器(ApplicationListener)
spring中的事件監聽器同樣有一個頂級接口ApplicationListener,只有一個onApplicationEvent(E event)方法,當該監聽器所監聽的事件發生時,就會執行該方法
1.3、Spring中的事件發布者(ApplicationEventPublisher)
spring中的事件發布者同樣有一個頂級接口ApplicationEventPublisher,只有一個方法publishEvent(Object event)方法,調用該方法就可以發生spring中的事件
1.4、Spring中的事件廣播器(ApplicationEventMulticaster)
spring中的事件核心控制器叫做事件廣播器,接口為ApplicationEventMulticaster,廣播器的作用主要有兩個:
作用一:將事件監聽器注冊到廣播器中,這樣廣播器就知道了每個事件監聽器分別監聽什么事件,且知道了每個事件對應哪些事件監聽器在監聽
作用二:將事件廣播給事件監聽器,當有事件發生時,需要通過廣播器來廣播給所有的事件監聽器,因為生產者只需要關心事件的生產,而不需要關心該事件都被哪些監聽器消費。
二、Spring事件監聽機制的使用
以電商為例,假設現在有這樣一個場景:當用戶下單成功之后,此時需要做很多操作,比如需要保存一個訂單記錄、對應的商品庫存需要扣除,假設下單的時候還用到了紅包,那么對應的紅包也需要改成已經使用。所以相當於一個下單操作,需要進行三個數據更新操作。而這三個操作實際上又是互相沒有任何關聯的,所以可以通過三個下單事件的監聽器分別來處理對應的業務邏輯,此時就可以采用Spring的事件監聽機制來模擬實現這樣的場景。
1、首先定義一個下單事件 OrderEvent,下單事件中包含了訂單號、商品編號和使用的紅包編號,代碼如下:
1 /** 2 * @Auther: Lucky 3 * @Date: 2020/7/8 下午2:53 4 * @Desc: 自定義下單事件 5 */ 6 public class OrderEvent extends ApplicationEvent { 7 8 /** 訂單編號*/ 9 private String orderCode; 10 /** 商品編號*/ 11 private String goodsCode; 12 /** 紅包編號*/ 13 private String redPacketCode; 14 15 /** 事件的構造函數*/ 16 public OrderEvent(ApplicationContext source, String orderCode, String goodsCode, String redPacketCode) { 17 super(source); 18 this.orderCode = orderCode; 19 this.goodsCode = goodsCode; 20 this.redPacketCode = redPacketCode; 21 } 22 }
2、分別定義訂單監聽器、商品監聽器和紅包監聽器分別監聽下單事件,分別做對應的處理,代碼如下:
1 /** 2 * @Desc: 訂單監聽器(用於保存訂單信息) 3 */ 4 public class OrderListener implements ApplicationListener<OrderEvent> { 5 6 @Override 7 public void onApplicationEvent(OrderEvent event) { 8 System.out.println("訂單監聽器監聽到下單事件,訂單號為:" + event.getOrderCode()); 9 //TODO 保存訂單處理11 } 12 }
1 /** 2 * @Desc: 商品監聽器(用於更新商品的庫存) 3 */ 4 public class GoodsListener implements ApplicationListener<OrderEvent> { 5 6 @Override 7 public void onApplicationEvent(OrderEvent event) { 8 System.out.println("商品監聽器監聽到下單事件,更新商品庫存:" + event.getGoodsCode()); 9 //TODO 更新商品庫存11 } 12 }
/** * @Desc: 紅包監聽器(用於使用紅包) */ public class RedPacketListener implements ApplicationListener<OrderEvent> { @Override public void onApplicationEvent(OrderEvent event) { if(event.getRedPacketCode()!=null) { System.out.println("紅包監聽器監聽到下單事件,紅包編號為:" + event.getRedPacketCode()); //TODO 使用紅包處理 }else { System.out.println("訂單:"+event.getOrderCode()+"沒有使用紅包"); } } }
3、事件發布器和事件廣播器無需自定義,采用Spring默認的就可以
4、現在事件、事件監聽器、事件發布器和事件廣播器都已經定義完畢,接下來就需要將事件監聽器和事件的監聽關系注冊到Spring容器中即可,方法很簡單,實際就是將事件監聽器當作是一個bean注冊到Spring容器即可,如下示:
1 <beans> 2 3 <!-- 其他bean --> 4 5 <bean id="orderListener" class="com.lucky.test.spring.event.OrderListener"/> 6 <bean id="goodsListener" class="com.lucky.test.spring.event.GoodsListener"/> 7 <bean id="redPacketListener" class="com.lucky.test.spring.event.RedPacketListener"/> 8 </beans>
5.測試代碼如下:
1 public static void main(String[] args){ 2 /** 1.初始化Spring容器 */ 3 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); 4 /** 2. 模擬創建下單事件*/ 5 for (int i=0;i < 5;i++){ 6 String orderCode = "test_order_" + i; 7 String goodsCode = "test_order_" + i; 8 String redPacketCode = null; 9 if(i%2==0) { 10 //偶數時使用紅包 11 redPacketCode = "test_order_" + i; 12 } 13 OrderEvent orderEvent = new OrderEvent(context, orderCode, goodsCode, redPacketCode); 14 /**3. ApplicationContext實現了ApplicationEventPublisher接口,所以可以直接通過ApplicationContext來發送事件*/ 15 context.publishEvent(orderEvent); 16 } 17 }
測試代碼很簡單,第一步是先初始化Spring容器,第二步是創建下單事件,第三部就是通過ApplicationContext直接發送事件,此時三個事件監聽器就可以監聽到下單事件並處理了,測試結果如下:
1 訂單監聽器監聽到下單事件,訂單號為:test_order_0 2 商品監聽器監聽到下單事件,更新商品庫存:test_order_0 3 紅包監聽器監聽到下單事件,紅包編號為:test_order_0 4 5 訂單監聽器監聽到下單事件,訂單號為:test_order_1 6 商品監聽器監聽到下單事件,更新商品庫存:test_order_1 7 訂單:test_order_1沒有使用紅包 8 9 訂單監聽器監聽到下單事件,訂單號為:test_order_2 10 商品監聽器監聽到下單事件,更新商品庫存:test_order_2 11 紅包監聽器監聽到下單事件,紅包編號為:test_order_2 12 13 訂單監聽器監聽到下單事件,訂單號為:test_order_3 14 商品監聽器監聽到下單事件,更新商品庫存:test_order_3 15 訂單:test_order_3沒有使用紅包 16 17 訂單監聽器監聽到下單事件,訂單號為:test_order_4 18 商品監聽器監聽到下單事件,更新商品庫存:test_order_4 19 紅包監聽器監聽到下單事件,紅包編號為:test_order_4
Tip:Spring的事件監聽機制是同步處理的,也就是說生產者發布事件和消費者消費事件是在同一個線程下執行的,所以本案例中的下單事件雖然按三個事件監聽器分別監聽下單事件,但是總的方法耗時並沒有減少,並且如果任何一個監聽器拋了異常,同樣會影響到其他的監聽器,所以每個事件監聽器監聽消息時必須要對事件進行異常捕獲操作,或者內部改成異步處理。
比如將監聽器改造如下:
1.在發布事件和消費事件出分別打印當前線程;2.紅包監聽器不做非空判斷,會導致redPacketCode為空時拋異常,測試結果如下:
1 發布事件線程為:main 2 訂單監聽器監聽到下單事件,訂單號為:test_order_0線程為:main 3 商品監聽器監聽到下單事件,更新商品庫存:test_order_0線程為:main 4 紅包監聽器監聽到下單事件,更新紅包:test_order_0線程為:main 5 發布事件線程為:main 6 訂單監聽器監聽到下單事件,訂單號為:test_order_1線程為:main 7 商品監聽器監聽到下單事件,更新商品庫存:test_order_1線程為:main 8 紅包監聽器監聽到下單事件,更新紅包:null線程為:main 9 Exception in thread "main" java.lang.NullPointerException 10 at com.lucky.test.spring.event.RedPacketListener.onApplicationEvent(RedPacketListener.java:15) 11 at com.lucky.test.spring.event.RedPacketListener.onApplicationEvent(RedPacketListener.java:10) 12 at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) 13 at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) 14 at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) 15 at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403) 16 at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360) 17 at com.lucky.test.spring.MainTest.main(MainTest.java:29)
從測試結果可以看出,發布事件和所有消費事件的監聽器執行的線程都是同一個線程,也就是本案例中的main線程,所以Spring的整個事件監聽機制實際上也是同步操作,都是由發布事件的線程來處理的。
而且一旦某一個監聽器拋異常了,就相當於整個線程都中止了,不僅后面的監聽器無法消費事件,連發布事件的線程也會收到影響。當多個監聽器之間的業務邏輯互相不影響時,可以采用優化方案,如下圖示:
此時就可以將事件監聽器改成異步處理方式,也就是同步接收事件消息,但是處理業務邏輯改成異步處理,比如上例中的訂單監聽器改造如下:
1 public class OrderListener implements ApplicationListener<OrderEvent> { 2 3 @Override 4 public void onApplicationEvent(OrderEvent event) { 5 /** 通過try/catch保證事件監聽器不會拋異常*/ 6 try { 7 new Thread(new Runnable() { 8 @Override 9 public void run() { 10 System.out.println("訂單監聽器監聽到下單事件,訂單號為:" + event.getOrderCode() + "線程為:" + Thread.currentThread().getName()); 11 //TODO 保存訂單處理 12 } 13 }).start(); 14 }catch (Exception e){ 15 e.printStackTrace(); 16 } 17 } 18 }
通過新建線程的方式去異步執行業務邏輯,或者將業務邏輯交給線程池執行也行。
5.對於同一個事件如果有多個事件監聽器時,既然是同步的,那么就必然會有執行順序的區別,Spring默認的事件執行的執行順序是按照bean加載的順序執行的,比如本例中,在XML中配置的順序是OrderListener->GoodsListener->RedPacketListener,
那么最后執行的順序就是這個順序,但是很顯然這種隱式的排序方式很容易讓開發人員忽視,所以Spring提供了額外的排序方式,就是讓監聽器實現Ordered接口或者Ordered的子接口PriorityOrdered接口
Ordered接口只有一個方法
/** 最高優先級*/ int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; /** 最低優先級*/ int LOWEST_PRECEDENCE = Integer.MAX_VALUE; /** 獲取優先級值,值越小優先級越高*/ int getOrder();
而Ordered接口的子接口PriorityOrdered繼承之Ordered接口,但是沒有任何實現,也就是說PriorityOrdered的作用僅僅就是一個標識的作用。
在Spring事件監聽機制中,優先級是優先按PriorityOrdered排序,然后再按Ordered排序,最終就按bean的加載順序排序
比如上例中的三個監聽器,由於OrderListener是最先加載的,所以默認是最先執行的,此時我們分別在GoodsListener和RedPacketListener實現Ordered接口和PriorityOrdered接口,分別如下:
1 public class GoodsListener implements ApplicationListener<OrderEvent>, Ordered { 2 @Override 3 public int getOrder() { 4 /** 使用最高優先級*/ 5 return Ordered.HIGHEST_PRECEDENCE; 6 } 7 }
1 public class RedPacketListener implements ApplicationListener<OrderEvent>, PriorityOrdered { 2 3 @Override 4 public int getOrder() { 5 /**使用最低優先級*/ 6 return Ordered.LOWEST_PRECEDENCE; 7 } 8 }
這里GoodsListener實現了Ordered接口,優先級為最高優先級;RedPacketListener實現了PriorityOrdered,設置優先級為最低優先級,執行結果如下:
1 紅包監聽器監聽到下單事件,更新紅包:test_order_0 2 商品監聽器監聽到下單事件,更新商品庫存:test_order_0 3 訂單監聽器監聽到下單事件,訂單號為:test_order_0
可以發現實現了PriorityOrdered接口的RedPacketListener最先執行,實現了Ordered接口的GoodsListener第二個執行,沒有實現排序接口的OrderListener接口最后執行。
這里雖然RedPacketListener和GoodsListener都實現了getOrder()方法,並且GoodsListener設置為優先級最高和RedPacketListener的優先級最低,但是還是會先執行RedPacketListener,這就是實現了PriorityOrdered接口的優勢。
實現了PriorityOrdered接口的監聽器無論優先級值如何都肯定會在實現了Ordered接口的監聽器優先執行,這也就是PriorityOrdered接口沒有任何實現的原因,這個接口僅僅是為了標記的作用,標記這個監聽器是最優先執行的。
三、Spring事件監聽機制的實現原理
通過上面的例子,已經了解了事件和事件監聽器的處理邏輯,但是還有很多問題需要去探究,比如事件的發布過程、監聽器是如何監聽消息的等等,此時就需要從ApplicationContext的初始化開始說起了。
當ApplicationContext初始化的時候, 有兩個核心步驟和事件監聽器有關,一個是初始化事件廣播器,一個是注冊所有的事件監聽器
1 /** 其他流程*/ 2 3 /** 初始化事件廣播器*/ 4 initApplicationEventMulticaster(); 5 6 /**其他流程*/ 7 8 /** 注冊事件監聽器*/ 9 registerListeners(); 10 11 /** 其他流程*/
3.1、初始化事件廣播器源碼解析
1 /** Spring容器的事件廣播器對象*/ 2 private ApplicationEventMulticaster applicationEventMulticaster; 3 4 /** 事件廣播器對應的beanName*/ 5 public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster"; 6 7 /** 初始化事件廣播器*/ 8 protected void initApplicationEventMulticaster() { 9 //1.獲取Spring容器BeanFactory對象 10 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 11 //2.從BeanFactory獲取事件廣播器的bean,如果存在說明是用戶自定義的事件廣播器 12 if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { 13 //2.1.給容器的事件廣播器賦值 14 this.applicationEventMulticaster = 15 beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); 16 if (logger.isTraceEnabled()) { 17 logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); 18 } 19 } 20 else { 21 //3.如果沒有自定義的,則初始化默認的事件廣播器SimpleApplicationEventMulticaster對象 22 this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); 23 //4.注冊該bean 24 beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); 25 if (logger.isTraceEnabled()) { 26 logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " + 27 "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); 28 } 29 } 30 }
從源碼可以看出,初始化事件廣播器的邏輯比較簡單,就是先給容器的事件廣播器applicationEventMulticaster對象賦值的過程,如果beanFactory中存在用於自定義的就使用自定義的,如果沒有自定義的就創建新的默認的事件廣播器SimpleApplicationEventMulticaster對象,然后賦值給applicationEventMulticaster對象。
3.2、注冊事件監聽器源碼解析
1 /** 注冊事件監聽器*/ 2 protected void registerListeners() { 3 //1.遍歷將通過編碼方式創建的事件監聽器加入到事件廣播器中 4 for (ApplicationListener<?> listener : getApplicationListeners()) { 5 //2.獲取到當前事件廣播器,添加事件監聽器 6 getApplicationEventMulticaster().addApplicationListener(listener); 7 } 8 9 //3.從BeanFactory中獲取所有實現了ApplicationListener接口的bean,遍歷加入到事件廣播器中 10 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); 11 for (String listenerBeanName : listenerBeanNames) { 12 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); 13 } 14 15 //3.獲取需要提前發布的事件 16 Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; 17 this.earlyApplicationEvents = null; 18 if (earlyEventsToProcess != null) { 19 for (ApplicationEvent earlyEvent : earlyEventsToProcess) { 20 //5.遍歷將提前發布的事件廣播出去 21 getApplicationEventMulticaster().multicastEvent(earlyEvent); 22 } 23 }
從源碼上看,注冊事件監聽器的邏輯也不復雜,主要是從容器中找到所有的事件監聽器,然后調用事件廣播器的addApplicationListener方法將事件監聽器添加到事件廣播器中,接下來再看下事件廣播器添加到邏輯,源碼如下:
1 @Override 2 public void addApplicationListener(ApplicationListener<?> listener) { 3 synchronized (this.retrievalMutex) { 4 // 將事件監聽器加入的內部的監聽器集合applicationListeners中 6 Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); 7 if (singletonTarget instanceof ApplicationListener) { 8 this.defaultRetriever.applicationListeners.remove(singletonTarget); 9 } 10 this.defaultRetriever.applicationListeners.add(listener); 11 this.retrieverCache.clear(); 12 } 13 }
可以看出注冊的邏輯比較簡單,就是將Listener對象加入到事件廣播器內部的集合中保存起來,這樣事件廣播器就保存了容器中所有的事件監聽器了。
3.3、事件的發布和消費原理
事件的發布是通過ApplicationEventPublisher的實現類實現的publishEvent方法實現的,ApplicationContext就實現了該接口,所以使用Spring時就可以直接使用ApplicationContext實例來調用publishEvent方法來發布事件,源碼如下:
1 /** 發布事件 2 * @param event:事件對象 3 * */ 4 @Override 5 public void publishEvent(Object event) { 6 publishEvent(event, null); 7 } 8 9 /** 發布事件 10 * @param event:事件對象 11 * @param eventType:事件類型 12 * */ 13 protected void publishEvent(Object event, @Nullable ResolvableType eventType) { 14 Assert.notNull(event, "Event must not be null"); 15 16 /** 1.將發布的事件封裝成ApplicationEvent對象(因為傳入的參數是Object類型,有可能沒有繼承ApplicationEvent) */ 17 ApplicationEvent applicationEvent; 18 if (event instanceof ApplicationEvent) { 19 applicationEvent = (ApplicationEvent) event; 20 } 21 else { 22 applicationEvent = new PayloadApplicationEvent<>(this, event); 23 if (eventType == null) { 24 eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); 25 } 26 } 27 28 if (this.earlyApplicationEvents != null) { 29 /** 2.1.如果需要提前發布的事件還沒有發布完,則不是立即發布,而是將事件加入到待發布集合中*/ 30 this.earlyApplicationEvents.add(applicationEvent); 31 } 32 else { 33 /** 2.2.獲取當前的事件廣播器,調用multicasterEvent方法廣播事件*/ 34 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 35 } 36 37 /** 3.如果當前applicationContext有父類,則再調用父類的publishEvent方法*/ 38 if (this.parent != null) { 39 if (this.parent instanceof AbstractApplicationContext) { 40 ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); 41 } 42 else { 43 this.parent.publishEvent(event); 44 } 45 } 46 }
其實這里的邏輯也比較簡單,首先是將發布的事件轉化成ApplicationEvent對象,然后獲取到事件廣播器,調用事件廣播器的multicastEvent方法來廣播事件,所以核心邏輯又回到了事件廣播器那里
1 /** 廣播事件 2 * @param event:事件 3 * @param eventType:事件類型 4 * */ 5 @Override 6 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { 7 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); 8 Executor executor = getTaskExecutor();(如果有Executor,則廣播事件就是通過異步來處理的) 9 /** 10 * 1.根據事件和類型調用getApplicationListeners方法獲取所有監聽該事件的監聽器 11 * */ 12 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { 13 if (executor != null) { 14 /** 2. 異步遍歷執行invokeListener方法來喚醒監聽器處理事件 */ 15 executor.execute(() -> invokeListener(listener, event)); 16 } 17 else { 18 invokeListener(listener, event); 19 } 20 } 21 }
這里主要有兩個核心步驟,首先是根據事件和類型找到監聽了該事件的所有事件監聽器;然后遍歷來執行監聽器的處理邏輯.另外如果配置了執行器Executor,就會通過Executor來異步發布事件給監聽器
1、根據事件獲取事件監聽器源碼如下:
1 protected Collection<ApplicationListener<?>> getApplicationListeners( 2 ApplicationEvent event, ResolvableType eventType) { 3 4 Object source = event.getSource(); 5 Class<?> sourceType = (source != null ? source.getClass() : null); 6 ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); 7 8 // Quick check for existing entry on ConcurrentHashMap... 9 ListenerRetriever retriever = this.retrieverCache.get(cacheKey); 10 if (retriever != null) { 11 return retriever.getApplicationListeners(); 12 } 13 14 if (this.beanClassLoader == null || 15 (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && 16 (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { 17 // Fully synchronized building and caching of a ListenerRetriever 18 synchronized (this.retrievalMutex) { 19 retriever = this.retrieverCache.get(cacheKey); 20 if (retriever != null) { 21 return retriever.getApplicationListeners(); 22 } 23 retriever = new ListenerRetriever(true); 24 Collection<ApplicationListener<?>> listeners = 25 retrieveApplicationListeners(eventType, sourceType, retriever); 26 this.retrieverCache.put(cacheKey, retriever); 27 return listeners; 28 } 29 } 30 else { 31 // No ListenerRetriever caching -> no synchronization necessary 32 return retrieveApplicationListeners(eventType, sourceType, null); 33 } 34 }
這里核心方法是retrieveApplicationListeners(eventType, sourceType, retriever)方法,源碼如下:
1 private Collection<ApplicationListener<?>> retrieveApplicationListeners( 2 ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { 3 4 List<ApplicationListener<?>> allListeners = new ArrayList<>(); 5 Set<ApplicationListener<?>> listeners; 6 Set<String> listenerBeans; 7 synchronized (this.retrievalMutex) {
/** 初始化所有事件監聽器,存入集合中*/ 8 listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); 9 listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); 10 } 11 12 // Add programmatically registered listeners, including ones coming 13 // 遍歷所有監聽器,調用supportsEvent判斷是否監聽該事件 14 for (ApplicationListener<?> listener : listeners) { 15 if (supportsEvent(listener, eventType, sourceType)) { 16 if (retriever != null) { 17 retriever.applicationListeners.add(listener); 18 }
/** 如果監聽器監聽當前事件,則加入到監聽器集合中*/ 19 allListeners.add(listener); 20 } 21 } 22 23 // Add listeners by bean name, potentially overlapping with programmatically 24 // registered listeners above - but here potentially with additional metadata. 25 if (!listenerBeans.isEmpty()) { 26 ConfigurableBeanFactory beanFactory = getBeanFactory(); 27 // 28 for (String listenerBeanName : listenerBeans) { 29 try { 30 if (supportsEvent(beanFactory, listenerBeanName, eventType)) { 31 ApplicationListener<?> listener = 32 beanFactory.getBean(listenerBeanName, ApplicationListener.class); 33 if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { 34 if (retriever != null) { 35 if (beanFactory.isSingleton(listenerBeanName)) { 36 retriever.applicationListeners.add(listener); 37 } 38 else { 39 retriever.applicationListenerBeans.add(listenerBeanName); 40 } 41 } 42 allListeners.add(listener); 43 } 44 } 45 else { 46 // Remove non-matching listeners that originally came from 47 // ApplicationListenerDetector, possibly ruled out by additional 48 // BeanDefinition metadata (e.g. factory method generics) above. 49 Object listener = beanFactory.getSingleton(listenerBeanName); 50 if (retriever != null) { 51 retriever.applicationListeners.remove(listener); 52 } 53 allListeners.remove(listener); 54 } 55 } 56 catch (NoSuchBeanDefinitionException ex) { 57 // Singleton listener instance (without backing bean definition) disappeared - 58 // probably in the middle of the destruction phase 59 } 60 } 61 } 62 63 /** 將所有監聽器根據Order進行排序*/ 64 AnnotationAwareOrderComparator.sort(allListeners); 65 if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { 66 retriever.applicationListeners.clear(); 67 retriever.applicationListeners.addAll(allListeners); 68 } 69 return allListeners; 70 }
代碼比較多,但是核心步驟其實就三步,
第一步:獲取事件廣播器中所有的事件監聽器
第二步:遍歷事件監聽器,判斷該監聽器是否監聽當前事件
第三步:將所有監聽當前事件的監聽器進行排序
其中第二步判斷監聽器是否監聽事件的判斷,主要是通過反射獲取該監聽器實現的接口泛型類,如果包含當前事件的類則表示監聽,否則就表示不監聽
2、喚醒監聽器處理事件源碼如下:
1 protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { 2 ErrorHandler errorHandler = getErrorHandler(); 3 if (errorHandler != null) { 4 try { 5 /** 調用doInvokeListener方法*/ 6 doInvokeListener(listener, event); 7 } 8 catch (Throwable err) { 9 errorHandler.handleError(err); 10 } 11 } 12 else { 13 /** 調用doInvokeListener方法*/ 14 doInvokeListener(listener, event); 15 } 16 }
1 private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { 2 try { 3 /** 直接調用ApplicationListener的onApplicationEvent(event)方法*/ 4 listener.onApplicationEvent(event); 5 } 6 catch (ClassCastException ex) { 7 String msg = ex.getMessage(); 8 if (msg == null || matchesClassCastMessage(msg, event.getClass())) { 9 // Possibly a lambda-defined listener which we could not resolve the generic event type for 10 // -> let's suppress the exception and just log a debug message. 11 Log logger = LogFactory.getLog(getClass()); 12 if (logger.isTraceEnabled()) { 13 logger.trace("Non-matching event type for listener: " + listener, ex); 14 } 15 } 16 else { 17 throw ex; 18 } 19 } 20 }
可以看出喚醒的邏輯比較簡單,直接調用監聽器的onApplicationEvent(E event)方法即可。