目標:
1. 監聽器如何使用
2. 監聽器的原理
3. 監聽器的類型
4. 多播器的概念和作用
5. 接口類型的監聽器是如何注冊的?
6. 注解類型的監聽器和如何注冊的?
7. 如果想在所有的bean都加載完成以后做一些事情, 怎么辦?
一. 監聽器的使用
為什么要學習監聽器呢?學習監聽器主要學習監聽器的設計思想。 比如,我們之前研究過的nacos,他就是使用監聽器進行集成的。所以了解監聽器的原理,就很重要了。
首先, 我們要知道監聽器如何使用。
1.1 Spring事件的原理
原理: 是觀察者模式
Spring的事件監聽有三個組成部分:
1. 事件(ApplicationEvent):要廣播,發送的消息. 監聽器監聽的事情
2. 監聽器(ApplicationListener): 觀察者模式中的觀察者, 監聽器監聽特定事件, 並在內部定義了事件發生后的相應邏輯.
3. 事件發布器(ApplicationEventMulticaster):對應於觀察者模式中的被觀察者/主題.負責通知觀察者. 對外提供發布事件和增刪事件監聽器的接口.維護事件和事件監聽器之間的關系.並在事件發生時負責通知事件監聽器.
1.2 認識監聽器
上面認識了監聽器. 接下來看一個例子. 通過例子來理解.
就好比現在有一個消息, 比如說: 下單后減庫存. 減庫存就是一個事件, 這個事件需要一個事件播放器, 將事件播放出去. 然后另一端事件監聽器, 接收到信息,進行處理.
比如:下面的demo
有一個訂單Order :
package com.lxl.www.events; /** * Description * * DATE 2020/11/17. * * @author lxl. */ public class Order { private Integer id; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
接下來, 有一個訂單事件. 訂單的操作,帶來的庫存的增減. 就是一個訂單事件
package com.lxl.www.events; import org.springframework.context.ApplicationEvent; import java.io.Serializable; /** * Description * 訂單的事件 * * 事件的分類: 分為自定義事件和內置事件 * DATE 2020/11/17. * * @author lxl. */ public class OrderEvent extends ApplicationEvent implements Serializable { private static final long serialVersionUID = 1L; private String name; public OrderEvent(Object event, String name) { super(event); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
第三: 事件監聽器 ,事件監聽器用來監聽事件. 當OrderEvent發布減庫存消息的時候, 事件監聽器就能聽到.
package com.lxl.www.events; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * Description * OrderEvent的事件監聽器 * * * DATE 2020/11/17. * * @author lxl. */ @Component public class OrderEventListenter implements ApplicationListener<OrderEvent> { /** * 當某一個事件發布的時候, 就會觸發事件監聽器 * @param event the event to respond to */ @Override public void onApplicationEvent(OrderEvent event) { if (event.getName().equals("減庫存")) { System.out.println("事件監聽器 監聽到 減庫存"); } } }
是不是和mq相差不多.
mq也是一個訂閱者,一個發布者.
下面寫一個main方法, 運行看看監聽器的效果
package com.lxl.www.events; import org.springframework.beans.factory.parsing.SourceExtractor; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * 監聽器的使用 */ public class MainClass { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); /** * 使用場景: 比如有一個訂單, 由用戶下單了,那么對應的就要減庫存.其實下單和減庫存不需要是串行. * 通常, 我們會使用一個mq去處理減庫存的情況. 也就是采用異步的方式. * * 那么, 監聽器的遠離和mq是類似的. 我們可以手動設置采用同步還是異步的方式處理. */ Order order = new Order(); order.setId(1); System.out.println("下單"); // 發布事件. 當在這里發布事件, 那么就會被事件監聽器監聽到 ctx.publishEvent(new OrderEvent(order, "減庫存")); System.out.println("日志....."); } }
輸出結果
下單
事件監聽器 監聽到 減庫存
日志.....
監聽器使用的設計模式是: 觀察者模式.
1.3 監聽器的類型
監聽器有兩種類型: 一種是內置的監聽器, 一種是自定義監聽器.
1.3.1 內置監聽器
spring設置了一個內置監聽器的父類.
public abstract class ApplicationContextEvent extends ApplicationEvent { /** * Create a new ContextStartedEvent. * @param source the {@code ApplicationContext} that the event is raised for * (must not be {@code null}) */ public ApplicationContextEvent(ApplicationContext source) { super(source); } /** * Get the {@code ApplicationContext} that the event was raised for. */ public final ApplicationContext getApplicationContext() { return (ApplicationContext) getSource(); } }
實現了ApplicationContextEvent的類就是內置的監聽器. 我們使用快捷鍵ctrl + H, 查看都有哪些類實現了 ApplicationContextEvent
一共有5各類實現了ApplicationContextEvent.
Event | 說明 |
ContextRefreshEvent | 當容器被實例化或者refresh時發布.如調用refresh()方法. 此處的實例化是指所有的bean都已被加載,后置處理器都被激活,所有單例bean都已被實例化,所有的容器對象 都已經准備好可使用. 如果容器支持熱重載,則refresh()可以被觸發多次(XmlWebApplicationContext支持熱刷新, 而GenericApplicationContext不支持熱刷新) |
ContextStartedEvent | 當容器啟動時發布, 即調用start()方法, 已啟用意味着所有的lifecycle都已顯示收到了start的信號 |
ContextStoppedEvent | 當容器停止時發布. 即調用stop()方法, 既所有的lifecycle bean都已顯示接收了stop信號, 關閉的容器可以通過start()方法重啟 |
ContextClosedEvent | 當容器關閉時發布. 即調用close()方法, 關閉意味着所有的單例bean都已被銷毀. 關閉的容器不能被重啟或refresh() |
1. ContextRefreshEvent: 當容器被實例化或者refresh時發布
我們來看看一下源碼.
從refresh()源碼進入.
public AnnotationConfigApplicationContext(Class<?>... componentClasses) { // 進入構造函數, 首先調用自身的構造方法this(); // 調用自身的構造方法之前, 要先調用父類的構造方法 this(); // register配置注冊類 register(componentClasses); // ioc容器刷新接口--非常重要 refresh(); }
/** * refresh是spring最核心的方法, 里面包含了整個spring ioc的全過程, 包括spring加載bean到銷毀bean的全過程 * 學習spring, 就是學習里面的13個方法, 如果13個方法都學完了, 基本上就打通了 * @throws BeansException * @throws IllegalStateException */ @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准備刷新上下文環境 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //2. 獲取告訴子類初始化bean工廠, 不同工廠不同實現 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ...... // Last step: publish corresponding event. //最后容器刷新 發布刷新時間(spring cloud是從這里啟動的 ) finishRefresh(); } ...... } }
進入到finishRefresh()方法
protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). // 清除上下文緩存 clearResourceCaches(); // Initialize lifecycle processor for this context. // 注冊lifecycleProcessor聲明周期處理器 // 作用: 當ApplicationContext啟動或停止時, 他會通過LifecycleProcessor來與所有聲明的bean進行交互 initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. // 為實現了SmartLifeCycle並且isAutoStartup, 自動啟動的Lifecycle調用start()方法 getLifecycleProcessor().onRefresh(); // 發布容器啟動完畢事件 publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }
我們看到有一個發布事件. 這個事件的作用是通知容器已經啟動完畢. 注意看, 里面發布的是什么事件? new ContextRefreshedEvent(this). 發布的是ContextRefreshedEvent事件.
下面有一個問題: 怎么樣可以在所有的bean創建完以后做擴展代碼呢?
上面我們說到了, 當所有的bean都創建完以后, 會調用publishEvent(new ContextRefreshedEvent(this));發布容器啟動完畢的事件.
這時我們可以自定義一個監聽器, 用來監聽ContextRefreshedEvent事件.
/** * 自定義一個事件監聽器, 用來監聽ContextRefreshedEvent事件 */ @Component public class ContextRefreshedEventListener { /** * 聲明這是一個事件監聽器, 監聽的是ContextRefreshedEvent事件. * @param event */ @EventListener(ContextRefreshedEvent.class) public void onApplicationEvent(ContextRefreshedEvent event) { .... // 在所有的bean創建完以后, 寫一些邏輯代碼 } }
然后, 在里面寫上我們需要在容器都創建完畢之后執行的邏輯代碼.
2. ContextClosedEvent: 當容器關閉時發布
還是先來看源碼, spring是在何時發布的這個事件.
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); } }
在doClose()的時候, 發布了publishEvent(new ContextClosedEvent(this));事件
我們看一看具體發布的是什么事件呢? 就是ContextClosedEvent事件
假如: 我們想要在容器關閉的時候做一些擴展, 就可以寫一個監聽器, 在容器關閉的時候監聽ContextClosedEvent事件
Spring內置的事件, 我們就不用再自己定義了. 我們需要做的就是定義一個監聽器, 監聽事件就可以了.
1.3.2 自定義監聽器
不是spring定義的監聽器, 也就是我們自己定義的監聽器就是自定義監聽器. 下面來看看自定義監聽器的兩種類型.
類型一: 基於接口
@Component public class HelloEventListener implements ApplicationListener<OrderEvent> { @Override public void onApplicationEvent(OrderEvent event) { if (event.getName().equals("減庫存")) { System.out.println("減庫存...."); } } }
事件監聽器需要實現ApplicationListener接口, 這是一個泛型接口, 泛型的類型就是事件的類型.
其次, 這個監聽器需要是spring容器托管的bean, 因此加上了@Component注解, 里面只有一個方法onApplicationEvent, 就是事件觸發時執行的內容.
類型二: 基於注解
@Component public class OrderEventListener { @EventListener(OrderEvent.class) public void onApplicationEvent(OrderEvent event) { if (event.getName().equals("減庫存")) { System.out.println("減庫存...."); } } }
在方法上面添加注解@EventListener(OrderEvent.class) 監聽的是哪個事件呢?OrderEvent.class
我們在定義監聽器的時候, 可以選擇是基於接口的方式還是基於注解的方式.
二. 監聽器源碼
首先, 監聽器的聲明,調用,都是在refresh()方法里面進行,我們先來看看refresh()的整體脈絡. 其中標紅的部分是和監聽器有關系的模塊.
這里面的第五步, 第九步, 第十一步, 都詳細的分析過. 下面主要看看和監聽器有關的幾步.
2.1 准備上下文環境prepareRefresh()
在准備上下文環境的時候, 我們看看做了哪些事情
1. 設置了容器當期的狀態, 是激活狀態
2. 初始化了屬性源initPropertySources();.
在AbstractApplicationContext類中沒有實現這個方法. 這是一個父類定義的方法. 比如:我們可以自定義一個類, 然后重寫initPropertySource, 在改方法中設置一個環境變量abc, 那么在容器啟動的時候, 就會去環境變量中檢查, 是否環境變量中有這個屬性, 如果沒有就會拋出異常.
3. 接下來就是驗證上面環境變量中指定的屬性是否存在了. getEnvironment().validateRequiredProperties(); 不存在就拋出異常MissingRequiredPropertiesException
4. 然后接下來,和事件有關的一步, 創建了早期的事件監聽器
// 創建早期的事件監聽器. // Store pre-refresh ApplicationListeners... if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); }
這里有一個問題, 什么是早期的事件監聽器呢? 早對應的就是晚了. 早期指的是多早呢?
早期事件指的是事件監聽器還沒有注冊到事件多播器的時候.
早期定義的事件不需要手動的publishEvent, 在RegisterListener()階段會自動發布早期事件.
什么是早期的事件監聽器呢? 早對應的就是晚了. 早期指的是多早呢? 早期事件指的是事件監聽器還沒有注冊到事件多播器的時候. 早期定義的事件不需要手動的publishEvent, 在RegisterListener()階段會自動發布早期事件.
在這里就定義了一個集合, 這個集合就是后面事件監聽器集合. 在這里只是進行的初始化
5. 初始化保存早期事件的集合
this.earlyApplicationEvents = new LinkedHashSet<>();
在第一步: 對事件的操作就是初始化. 一共初始化了兩個集合, 一個是早期事件監聽器集合, 一個是早期的事件集合
2.2 初始化bean工廠
我們現在經常使用的beanFactory有兩種,一種是xml方式的, 另一種是注解方式的. 其實使用注解的更多一些. xml和注解方式的bean工廠在初始化的時候也是有區別的.
從上圖可以看出, 獲取兩種方式的bean工廠的區別
1. AbstractRefreshableApplicationContext: 基於xml配置文件的方式
2. GenericApplicationContext: 基於注解的方式.
基於注解實現的里面代碼很簡單, 只是刷新的beanFactory. 沒有耦合加載beanDefinition的流程. 基於xml實現的代碼, 里面耦合了加載beanDefinition
先來看看基於注解方式的, 基於注解方式只是指定了bean工廠的序列化ID
@Override protected final void refreshBeanFactory() throws IllegalStateException { if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts:
just call 'refresh' once"); } // 指定bean工廠的序列化ID this.beanFactory.setSerializationId(getId()); }
再來看看基於xml方式的, 基於xml方式的 除了指定了bean工廠的序列化id, 還耦合加載了beanDefinition
@Override protected final void refreshBeanFactory() throws BeansException { // 判斷bean工廠是否初始化過, 如果已經初始化過那么銷毀並關閉 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 重新創建一個bean工廠 DefaultListableBeanFactory beanFactory = createBeanFactory(); // 設置序列化id beanFactory.setSerializationId(getId()); // 設置個性化屬性 customizeBeanFactory(beanFactory); // 加載BeanDefinition loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " +
getDisplayName(), ex); } }
看上面的步驟.
1. 先看看是否已經有過工廠了, 如果已經有了,那么銷毀,關閉
2. 重新創建了一個空的新的工廠
3. 設置新工廠的序列化id
4. 設置個性化屬性bean
5. 加載bean定義. 我們看到, 使用xml方式會加載bean定義
6. 返回bean工廠對象
這一步: 主要是初始化了bean工廠
2.3 對bean工廠進行填充屬性prepareBeanFactory(beanFactory);
這一步是和監聽器有關系的. 我們先來看看源碼
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. // 設置bean工廠的類加載器為當前的application應用的加載器 beanFactory.setBeanClassLoader(getClassLoader()); // 為bean工廠設置標准的SPEL表達式解析器對象(StandardBeanExpressionResolver) beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 為bean工廠設置一個PropertiesEditor屬性資源編輯器(用於后面給bean對象賦值) beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. /** * 注冊一個完整的ApplicationContextAwareProcessor后置處理器, 用來處理ApplicationContextAware * ApplicationContextAwareProcessor是一個bean的后置處理器. 怎么使用呢? * * 在bean初始化完成以后, 會調用一堆的bean后置處理器. * 在初始化的地方,其實只調用了三個bean后置處理器. 那么其他的后置處理器是什么時候調用的呢? * 就是在這里, 這里注冊了 ApplicationContextAwareProcessor. * 在ApplicationContextAwareProcessor#invokeAwareInterfaces方法里調用了其他的aware * 那么invokeAwareInterfaces方法是在哪里調用呢? * 是在ApplicationContextAwareProcessor#postProcessBeforeInitialization調用的 * postProcessBeforeInitialization是在bean初始化之前會調用的后置處理器 * * 然后在通過addBeanPostProcessor()方法, 將bean的后置處理器添加到beanPostProcessors集合中 */ beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); /** * 忽略部分接口的函數方法, 在populateBean(創建bean的第二步:屬性賦值)時 * 因為下面接口都有set***方法, 這些方法不特殊處理將會自動注入到容器中. * * 忽略了這么多的Aware, 這是怎么回事呢? 因為Aware里面的方法都是以set開頭的. 當在創建bean, 設置屬性的時候, * 會給帶有set+屬性名的方法賦值. 而Aware的這些方法要忽略掉, 為什么忽略掉呢? * * 比如:EnvironmentAware 里面設置了一些環境變量, 這些環境變量是不需要進行屬性裝配的, 所以要把他們排除掉 */ beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. /** * 將beanFactory類型的實例注冊解析 * * 當注冊了依賴解析以后, 例如當注冊了對BeanFactory.class的解析依賴后, * 當bean屬性注入的時候, 一旦檢測到屬性為beanFactory類型. 便會將BeanFactory的實例注冊解析 * 為什么呢? * 比如: * @Autowired * ApplicationContext applicationContext 為什么能夠自動裝配, 通過@Autowired引入呢? 就是在這里裝配的. * 這個也是在注入屬性popularBean的時候體現的 * */ beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners. // 注冊事件監聽器探測器后置處理器接口, ApplicationListenerDetector 解析接口方式的監聽器 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Register default environment beans. if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
1. 設置bean工廠的類加載器為: 當前的application應用的加載器
2. 為bean工廠設置標准的SPEL表達式解析器對象, 這個解析器對象是誰呢? 就是StandardBeanExpressionResolver
3. 為bean工廠設置一個PropertiesEditor屬性資源編輯器, 用於后面給bean對象賦值
4. 給bean工廠注冊了一個ApplicationContextAwareProcessor后置處理器. 這里說說這個后置處理器類. 這個類有什么作用呢?
在bean初始化完成以后, 會調用一堆的bean后置處理器
在doCreateBean()中找到第三步: 初始化bean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { // 在初始化完成以后, 調用aware invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { // 在初始化的時候, 會調用很多的aware. invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 在初始化之前調用bean的后置處理器 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 調用初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // 再初始化之后調用bean的后置處理器 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
我們看到, 在初始化bean的時候, 調了很多Aware, invokeAwareMethods(beanName, bean);
/** * 這里主要有三類aware * @param beanName * @param bean */ private void invokeAwareMethods(final String beanName, final Object bean) { /** * 在這里調用的aware只有三類, 我們去BeanFactory中看, 他有一大堆的aware要調用, * 那么其他的aware是在哪里調用的呢? */ if (bean instanceof Aware) { // 實現了BeanNameAware的bean if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } // 實現了BeanClassLoaderAware接口 if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } // 實現了BeanFactoryAware if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
如上代碼, 我們看到, 其實知道用了3中類型的Aware. 分別是BeanNameAware, BeanClassLoaderAware 和 BeanFactoryAware.
那么其他的Aware呢? 我們看beanFactory接口的注釋可以看到, 會調用很多Aware
在初始化的地方,其實只調用了三個bean后置處理器. 那么其他的后置處理器是什么時候調用的呢?
就是在這里, 這里注冊了 ApplicationContextAwareProcessor.
在ApplicationContextAwareProcessor#invokeAwareInterfaces方法里調用了其他的aware
/** * 判斷bean是否實現了各種Aware * @param bean */ private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } }
而這個方法, 什么時候執行呢? 在初始化之前調用Bean的后置處理器執行的ApplicationContextAwareProcessor#postProcessBeforeInitialization
@Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){ return bean; } AccessControlContext acc = null; if (System.getSecurityManager() != null) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; }
然后在通過addBeanPostProcessor()方法, 將bean的后置處理器添加到beanPostProcessors集合中.
5. 忽略部分接口的函數方法. 這些接口主要是Aware.
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
忽略了這么多的Aware, 這是怎么回事呢?為什么忽略掉呢? 因為Aware里面的方法都是以set開頭的. 當在創建bean, 設置屬性的時候,
會給帶有set+屬性名的方法賦值.在populateBean(創建bean的第二步:屬性賦值)時 因為下面接口都有set***方法, 這些方法不特殊處理將會自動注入到容器中.
比如:EnvironmentAware 里面設置了一些環境變量, 這些環境變量是不需要進行屬性裝配的, 所以要把他們排除掉
6. 將beanFactory類型的實例注冊解析
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this);
當注冊了依賴解析以后, 例如當注冊了對BeanFactory.class的解析依賴后,
當bean屬性注入的時候, 一旦檢測到屬性為beanFactory類型. 便會將BeanFactory的實例注冊解析
為什么呢?
比如:
@Autowired
ApplicationContext applicationContext; .
為什么能夠自動裝配, 通過@Autowired引入呢? 就是在這里裝配的
這個也是在注入屬性popularBean的時候體現的
7. 注冊了一個解析接口方式的監聽器的 BeanPostProcessor.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
我們來看看ApplicationListenerDetector 類, 其下的 postProcessAfterInitialization方法, 是在createBean的第三步初始化之后執行的bean的后置處理器.
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof ApplicationListener) { // potentially not detected as a listener by getBeanNamesForType retrieval Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { // singleton bean (top-level or inner): register on the fly /* * 注冊接口類型的監聽器. 將其添加到applicationContext中 * 之所以要在這里在加一次, 是為了處理懶加載情況 */ 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; }
我們看這個方法, 方法一進來就判斷,是否是實現了ApplicationListener接口. 也就是說, 上面我們輸了注冊監聽器有兩種方式, 一種是接口方式, 另一種是注解方式. 這里解析的是實現接口的方式.
在這里,我們要先建立一個印象, 因為后面還會說到他. 為什么呢? 因為接口方式的監聽器在兩個地方被調用, 一個是這里, 另一個是在refresh()后面的流程registerListener()的時候. 那么, 為什么要有兩次調用監聽器呢? 我們后面再說
2.4 postProcessBeanFactory(beanFactory); 這是一個擴展方法, 可以初始化剩余的Aware.
我們是AbstractApplicationContext沒有實現, 但AbstractRefreshableWebApplicationContext類. 里面就定義了postProcessBeanFactory(beanFactory)
在里面注冊了ServletContextAwareProcessor
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
上面我們知道在 BeanFactory 里定義了需要調用的很多Aware. 但是有幾個Aware還沒有說到.
比如: ServletContextAware's {@code setServletContext} : ServletContextAware, 可以獲得當前的ServletContextAware
添加了這個Aware以后, 我們就可以實現一個ServletContextAware的接口.
到這里, 我們就知道所有的aware都在哪里被調用了.
2.5 調用bean工廠的后置處理器, 解析配置類
這一步就略過了,之前重點說過這一步
2.6 registerBeanPostProcessors(beanFactory); 注冊bean后置處理器, 這里主要是和AOP有關系
這里和監聽器關系不太大, 也暫時略過
2.7 initMessageSource(); 初始化國際化資源處理器
2.8 initApplicationEventMulticaster();創建事件多播器
事件多播器管理所有的事件監聽器. 並廣播事件給對應的監聽器
當我們調用ctx.publishEvent(new OrderEvent(order, "減庫存"))的時候. 就會去通知所有監聽了OrderEvent事件的事件監聽器, 那么, 是由誰去負責通知呢?
就是由EventMulticaster(事件多播器)將事件播報出去的.
首先, 判斷有沒有最定義的事件多播器. 如果有, 那么直接將其添加到容器中. 如果沒有, 就新建一個SimpleApplicationEventMulticaster類型的事件多播器, 然后將其添加到beanFactory中.
那么, 事件多播器都做了什么事情呢? 具體來看一看SimpleApplicationEventMulticaster類.
這是SimpleApplicationEventMulticaster的繼承結構. 繼承了AbstractApplicationEventMulticaster, 而AbstractApplicationEventMulticaster又實現了ApplicationEventMulticaster. 我們看看在ApplicationEventMulticaster中都對應了哪些接口
public interface ApplicationEventMulticaster { void addApplicationListener(ApplicationListener<?> listener); void addApplicationListenerBean(String listenerBeanName); void removeApplicationListener(ApplicationListener<?> listener); void removeApplicationListenerBean(String listenerBeanName); void removeAllListeners(); void multicastEvent(ApplicationEvent event); void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); }
看名字就知道了, 做了兩件事, 一個是管理事件監聽器, 另一個是廣播事件.
我們看AbstractApplicationEventMulticaster如何實現這幾個接口的
/** * AbstractApplicationEventMulticaster管理了所有的監聽器. * 當我們注冊一個監聽器以后, 就會通過addApplicationListener方法添加到事件多播器中. * @param listener the listener to add */ @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) { this.defaultRetriever.applicationListeners.remove(singletonTarget); } this.defaultRetriever.applicationListeners.add(listener); this.retrieverCache.clear(); } }
這是添加事件監聽器.
在SimpleApplicationEventMulticaster里面, 定義了廣播事件監聽器
@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); } } }
這里有兩種方式, 一種是同步的方式, 另一種是異步的方式. 根據設置的eventType來決定的. 其實異步的方式就是建立了一個新的線程
我么你來看一下調用事件監聽器廣播事件
invokeListener#doInvokeListener
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; } } }
最終調用的是監聽器的onApplicationEvent方法. 這個方法就是每一個監聽器都會自定義的方法.
listener.onApplicationEvent(event);
2.9 onRefresh();這是一個擴展方法. 這里沒有具體實現.spring boot也是從這個方法進行啟動
2.10 注冊監聽器registerListeners();
注冊監聽器這里一共做了三件事:
1. 將事件監聽器注冊到多播器上
2. 廣播早期的事件
3. 清空早期事件.
到此步驟之前, 上面都是有早期事件的, 后面就沒有早期事件了,因為這一步就都清空了. 后面也不會在進行自動廣播了, 自動廣播的就是早期事件.
protected void registerListeners() { // Register statically specified listeners first. /** * 第一步, 獲取所有的事件監聽器集合. * 通常, 這個時候, 事件監聽器集合是空的, 除非手動調用allApplicationListeners()注冊事件監聽器 */ for (ApplicationListener<?> listener : getApplicationListeners()) { // 將監聽器注冊到多播器上 getApplicationEventMulticaster().addApplicationListener(listener); } /** * 第二步: 注冊接口方式的監聽器 * 獲取beanDefinition中 ApplicationListener 類型的監聽器. 也就是說, 使用接口方式定義的監聽器
* 就可以在這里被注冊到多播器的 * 這里是從BeanDefinition中拿的, 我們自定義了 OrderEventListenter 監聽器, 那么會不會拿到呢? * 我們知道監聽器的實現有兩種方式, 一種是接口方式, 一種是注解方式. * 如果OrderEventListenter采用的是接口方式, 那么就可以拿到. 因為它實現了ApplicationListener.拿到了,
* 就把監聽器注冊到多播器上. * 如果是注解方式, 那就拿不到了 * */ String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { // 把監聽器注冊到多播器上 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } /** * 第三步: 獲取早期的事件, 然后廣播早期事件. * 這些早期事件是在第一步 prepareRefresh 注冊的. * */ Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; // 在這里將早期事件清空, 清空完以后, 就沒有早期事件了. this.earlyApplicationEvents = null; if (!CollectionUtils.isEmpty(earlyEventsToProcess)) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
如上源碼所示:
第一步. 獲取所有的事件監聽器集合, 通常這個時候, 事件監聽器的集合都是空的, 除非我們手動調用allApplicationListeners()注冊事件監聽器
第二步: 注冊接口方式的監聽器. 注意,是接口方式的. 通常我們自定義的監聽器. 有兩種類型, 接口方式和注解方式. 如果使用的是接口方式. 那么就是在這里被注冊的.如果是注解方式.不在這里注冊.
getBeanNamesForType(ApplicationListener.class, true, false);
掃描獲取ApplicationListener類型的監聽器.
然后將其注冊到多播器上. 我們知道多播器的兩個主要功能, 管理監聽器和廣播事件.
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
第三步: 獲取早期事件, 然后廣播早期事件. 早期事件我們之前已經說過了, 是在第一步prepareRefresh()方法里注冊的.
隨后, 立即清空早期事件集合. 然后廣播事件. 這樣早期定義好的事件就都被廣播出去了, 並且只能執行一次, 不會被再次執行.
/** * 第三步: 獲取早期的事件, 然后廣播早期事件. * 這些早期事件是在第一步 prepareRefresh 注冊的. * */ Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; // 在這里將早期事件清空, 清空完以后, 就沒有早期事件了. this.earlyApplicationEvents = null; if (!CollectionUtils.isEmpty(earlyEventsToProcess)) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } }
2.11 實例化剩余的單實例bean
這個方法就是循環遍歷BeanDefinitionMap, 調用getBean, 去生產剩余的bean, 之前詳細研究過這個步驟, 這里就不說了
2.12 完成refresh()操作, 發布刷新事件
protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). // 清除上下文緩存 clearResourceCaches(); // Initialize lifecycle processor for this context. // 注冊lifecycleProcessor聲明周期處理器 // 作用: 當ApplicationContext啟動或停止時, 他會通過LifecycleProcessor來與所有聲明的bean進行交互 initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. // 為實現了SmartLifeCycle並且isAutoStartup, 自動啟動的Lifecycle調用start()方法 getLifecycleProcessor().onRefresh(); // Publish the final event. // 發布容器refresh完畢的事件. // 發布的是什么事件呢? 是ContextRefreshedEvent事件. publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }
在這一步: 發布了容器Refreshed事件. 也就是容器啟動完成的事件.
到這里, 我們來看看publicshEvent的具體實現
/** * 發布事件給所有的監聽器 * Publish the given event to all listeners. * @param event the event to publish (may be an {@link ApplicationEvent} * or a payload object to be turned into a {@link PayloadApplicationEvent}) * @param eventType the resolved event type, if known * @since 4.2 */ 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 if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { /* * 調用事件多播器, 將這個事件發布出去 * 事件多播器是什么時候注冊的呢?
* 就是在refresh()初始化的時候, 調用initApplicationEventMulticaster(); 初始化的事件多播器 */ 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); } } }
這里做了如下幾件事
1. 獲取事件
2. 廣播事件
3. 廣播事件給父類監聽器.
詳細代碼可以看注釋
接下來看一下,具體的multicastEvent(...)
@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); } } }
這里首先會去獲取線程池. 看看有沒有重新定義線程池, 如果有這里executor就不是空的.
廣播事件有兩種形式, 一種是同步, 一種是異步. 如果executor線程池不空, 就以異步的形式廣播, 否則就以同步的形式廣播.
那么,我們如何自定義同步或者異步呢? 也是有兩種方式
第一種方式: 自定義事件多波器, 並指定taskExcutor
@Bean(name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster() { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); //ThreadPoolTaskExecutor // 這里指定了 taskExecutor, 就會使用異步的方式去執行 eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster; }
第二種方式, 在事件監聽器上使用注解@Async
@Component @Async public class OrderEventListenter implements ApplicationListener<OrderEvent> { /** * 當某一個事件發布的時候, 就會觸發事件監聽器 * @param event the event to respond to */ @Override public void onApplicationEvent(OrderEvent event) { if (event.getName().equals("減庫存")) { System.out.println("事件監聽器 監聽到 減庫存"); } } }
接下來看看如何廣播事件的.
/** * Invoke the given listener with the given event. * @param listener the ApplicationListener to invoke * @param event the current event to propagate * @since 4.1 */ 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; } } }
其實,具體執行的邏輯, 就是我們在監聽器中定義的onApplicationEvent(event)方法中的邏輯實現.
三. 注冊接口方式的監聽器
在上面的源碼分析中, 注冊接口方式的監聽器, 其實是由兩個地方.
第一個: 在第十步registerListener()
protected void registerListeners() { ..... /** * 第二步: 注冊接口方式的監聽器 * 獲取beanDefinition中 ApplicationListener 類型的監聽器.
* 也就是說, 使用接口方式定義的監聽器就可以在這里被注冊到多播器的 * 這里是從BeanDefinition中拿的, 我們自定義了 OrderEventListenter 監聽器, 那么會不會拿到呢? * 我們知道監聽器的實現有兩種方式, 一種是接口方式, 一種是注解方式. * 如果OrderEventListenter采用的是接口方式, 那么就可以拿到. 因為它實現了ApplicationListener.
* 拿到了, 就把監聽器注冊到多播器上. * 如果是注解方式, 那就拿不到了 * */ String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { // 把監聽器注冊到多播器上 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } ..... }
另一個: 是在第三步進行屬性填充的時候注冊的
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { ...... /** * 注冊事件監聽器探測器后置處理器接口, ApplicationListenerDetector 解析接口方式的監聽器 */ beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); ...... }
在准備屬性的方法里, 有一個注冊時間監聽器探測器后置處理. 在這個監聽器的探測器里面, 進行了注冊.
來看看ApplicationListenerDetector
class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor { ....../** * 初始化后的bean后置處理器 * * 這個方法是在 registerListener 之后執行的, 在registerListener()方法里注冊過一次接口方式的監聽器. * 在這里還會在注冊一次. * * 問題: 為什么同一個監聽器, 要在兩個地方注冊呢? * 第一次添加的是監聽器的名字, 第二次添加的是bean實體. 那為什么要添加兩次呢? * 這是為了處理帶有@Lazy懶加載方式的bean. 懶加載的bean是不會在初始化容器的時候創建bean的. * * 比如, 我給監聽器類加上一個@Lazy, 那么他就不會走bean的后置處理器, 因為bean的后置處理器, 是在bean創建過程中調用的. * 那什么時候會被調用呢? 在真正使用的時候. 比如調用 ctx.publishEvent(new OrderEvent(order, "減庫存")); * 馬上就要用到了, 所以, 這時候回去調bean的后置處理器. 執行代碼看一下效果 * * * @param bean the new bean instance * @param beanName the name of the bean * @return */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof ApplicationListener) { // potentially not detected as a listener by getBeanNamesForType retrieval Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { // singleton bean (top-level or inner): register on the fly /* * 注冊接口類型的監聽器. 將其添加到applicationContext中 * 之所以要在這里在加一次, 是為了處理懶加載情況 */ 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; } ....... }
我們看到在ApplicationListenerDetector中定義了方法postProcessAfterInitialization. 這個方法會在創建屬性的第三步執行完以后調用. 第三步是初始化. 看名字也知道是初始化之后調用的后置處理器. 在這里, 注冊了接口類型的監聽器
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
那么, 為什么要有兩次注冊呢?
其實這里是為了解決懶加載的問題. 因為,如果一個類是懶加載的類, 那么他只有真正被調用的時候才回去加載. 所以, 也就是在類進行初始化以后才會被調用. 因此在初始化之后再次加載了接口類型的監聽器.
四. 解析注解方式的監聽器
整個流程走完, 我們都只看到接口方式的監聽器注冊的地方. 那么注解類型的監聽器是什么時候被創建的呢?
首先, 注解是何時被解析的? 我們知道BeanDefinitionReader在解析創世紀的類的時候, 注冊了很多創世紀的類.其中就有兩個是用於負責處理@EventListener注解的
再來回顧一下這段代碼
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // 獲取到beanFactory DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); /** * 判斷beanFactory中是否有AnnotationAwareOrderComparator和ContextAnnotationAutowireCandidateResolver * 沒有則添加 */ if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof
ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // BeanDefinitionHolder: 為BeanDefinition設置名字和別名 Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); // 如果registry中沒有ConfigurationClassPostProcessor配置類后置處理器, 就添加一個 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); // 構建BeanDefinitionHolder, 並添加到beanDefs beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 如果rigistry中, 沒有AutowiredAnnotationBeanPostProcessor Autowired注解bean的后置處理器, 則添加一個 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); // 構建BeanDefinitionHolder, 並添加到beanDefs beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. // 檢查對JSR-250的支持, 如果rigistry中沒有 CommonAnnotationBeanPostProcessor 通用注解后置處理器, 則添加一個 if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); // 構建BeanDefinitionHolder, 並添加到beanDefs beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. // 檢查對jpa的支持, 如果不包含 internalPersistenceAnnotationProcessor, 持久化注解處理器, 就添加一個 if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " +
PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } /** * 下面這兩個注解是用來解析@EventListener的 */ // 檢查對事件監聽的支持, 如果不包含事件監聽處理器 internalEventListenerProcessor, 就添加一個 /* * EventListenerMethodProcessor : 既不是bean的后置處理器, 也不是bean工廠的后置處理器 * 那么EventListenerMethodProcessor是在哪里被調用,並且解析注解方式的監聽器呢? * * 下面看一下EventListenerMethodProcessor的繼承結構圖. * 1. 實現了SmartInitializingSingleton接口 : * 2. 實現了ApplicationContextAware接口 : 因為要往容器中注入bean, 所以,里面要使用容器的上下文,
* 將容器以Aware的方式set進來 * 3. 實現了BeanFactoryPostProcessor接口 : * * */ if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } // 如果不包含事件監聽工廠處理器 internalEventListenerFactory , 就添加一個 if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
如上圖就是注冊的創世紀的處理器. 最后兩個就是用來處理@EventListener注解的.
下面來看看EventListenerMethodProcessor事件監聽器處理器,
首先, 看一下EventListenerMethodProcessor的繼承結構圖.
EventListenerMethodProcessor實現了三個接口.
1.實現了SmartInitializingSingleton接口
2. 實現了ApplicationContextAware接口 : 因為要往容器中注入bean, 所以,里面要使用容器的上下文, 將容器以Aware的方式set進來
3. 實現了BeanFactoryPostProcessor接口
SmartInitializingSingleton接口在哪里被用到了呢?
在refresh()#finishBeanFactoryInitialization(beanFactory); 實例化剩余的單例bean的過程中
在DefaultListableBeanFactory#preInstantiateSingletons()方法. 有兩次循環遍歷beanNames
@Override public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. // 獲取容器中所有bean定義的名字 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... /** * 第一步: 循環bean定義的name, 創建bean */ for (String beanName : beanNames) { // 獲取bean定義 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 生產bean定義的條件: 不是抽象的, 是單例的, 不是懶加載的. 符合這個標准的, 最后才會調用getBean()生產bean if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 這里判斷是不是工廠bean, 這里和BeanFactory不是一個意思, 判斷當前這個bean是否實現了beanFactory的接口 if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { // 將bean轉換為FactoryBean 工廠bean final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { // 獲取bean getBean(beanName); } } } else { // 第二步: 調用getBean getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... /** * 第二步: 循環bean定義的name, 解析是否有實現了SmartInitializingSingleton接口的類 * 到這里, bean都已經被創建完了 */ for (String beanName : beanNames) { // 從緩存中得到實例instance Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
第一次循環, 是創建bean, 並獲取bean
第二次循環, 是在所有的bean都已經創建完以后, 如果singletonInstance是SmartInitializingSingleton的實例, 則調用afterSingletonsInstantiated()方法.
以下是EventListenerMethodProcessor#afterSingletonsInstantiated()方法實現
@Override public void afterSingletonsInstantiated() { // 從spring容器中獲取EventListenerFactoryBean ConfigurableListableBeanFactory beanFactory = this.beanFactory; Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set"); // 獲取所有類型的bean String[] beanNames = beanFactory.getBeanNamesForType(Object.class); for (String beanName : beanNames) { if (!ScopedProxyUtils.isScopedTarget(beanName)) { Class<?> type = null; try { type = AutoProxyUtils.determineTargetClass(beanFactory, beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (type != null) { if (ScopedObject.class.isAssignableFrom(type)) { try { Class<?> targetClass = AutoProxyUtils.determineTargetClass( beanFactory, ScopedProxyUtils.getTargetBeanName(beanName)); if (targetClass != null) { type = targetClass; } } catch (Throwable ex) { // An invalid scoped proxy arrangement - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target bean for scoped proxy '" +
beanName + "'", ex); } } } try { processBean(beanName, type); } catch (Throwable ex) { throw new BeanInitializationException("Failed to process @EventListener " + "annotation on bean with name '" + beanName + "'", ex); } } } } }
這里主要看processBean(beanName, type) 處理bean
private void processBean(final String beanName, final Class<?> targetType) { // 1. 是否包含注解@EventListener if (!this.nonAnnotatedClasses.contains(targetType) && AnnotationUtils.isCandidateClass(targetType, EventListener.class) && !isSpringContainerClass(targetType)) { Map<Method, EventListener> annotatedMethods = null; try { // 2. 查找@EventListener注解, 如果有則拿到標注@EventListener的方法 annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); } catch (Throwable ex) { // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); } } if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); } } else { // Non-empty set of methods ConfigurableApplicationContext context = this.applicationContext; Assert.state(context != null, "No ApplicationContext set"); // 3. 獲取bean工廠. 這個bean工廠是我們在創世紀的時候注冊的EventListenerFactory List<EventListenerFactory> factories = this.eventListenerFactories; Assert.state(factories != null, "EventListenerFactory List not initialized"); // 4. 循環遍歷有注解的方法 for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); //5. 創建事件監聽器 ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).
init(context, this.evaluator); } // 6. 將監聽器注入到多播器中 context.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } }
1. 首先判斷, 是否包含@EventListener注解
2. 查找@EventListener注解, 如果有則拿到標注@EventListener的方法
3. 獲取bean工廠. 這個bean工廠是我們在創世紀的時候注冊的EventListenerFactory
4. 循環遍歷有注解的方法
5. 創建事件監聽器
6. 將監聽器注入到多播器中
以上就是注解版的監聽器是如何注入到多播器中的.
五. 怎樣在所有的bean都創建完以后做擴展代碼?
第一種方式, 添加內置的監聽器, 類加載完以后, 調用監聽器
第二種方法. 就是在使用注解方式的時候, 實現SmartInitializingSingleton接口. 然后在bean實例化完成以后, 在調用