spring5/springboot2源碼學習 -- spring中的事件機制

介紹

spring內部實現了一套事件傳播機制的,也是觀察者模式的實現

主要角色

  • 事件
    在spring中一般就是ApplicationEvent對象(這是一個抽象類而不是接口)。spring中的常見事件:
    • ApplicationContextEvent:抽象類,ApplicationContext相關的事件。具體實現有:
      • ContextClosedEvent
      • ContextRefreshedEvent
      • ContextStartedEvent
      • ContextStoppedEvent
    • SpringApplicationEvent:抽象類,SpringApplication相關的事件,在springboot的啓動過程中逐個發佈。具體實現有:
      • ApplicationStartingEvent
      • ApplicationEnvironmentPreparedEvent
      • ApplicationFailedEvent
      • ApplicationPreparedEvent
      • ApplicationReadyEvent
    • ServletRequestHandledEvent:RequestHandledEvent的子類,每次請求處理完成後會發布這個事件
  • 事件發佈(傳播)器
    事件發佈器,在spring中對應ApplicationEventPublisher接口,其實現類就是各個ApplicationContext實現。接口定義如下:
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }
    void publishEvent(Object event);
}

並沒有啥值得提的地方,唯一的方法就是發佈事件。
雖然ApplicationEventPublisher對應的角色是事件發佈器,但其實具體的事件傳播行爲以及對listener的管理其實是由ApplicationEventMulticaster實現的,接口定義:

public interface ApplicationEventMulticaster {

    //管理listener相關的方法
    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);

}

spring中,ApplicationEventMulticaster的內置實現類只有一個:SimpleApplicationEventMulticaster

  • 監聽器
    對應spring中的ApplicationListener。接口定義:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

響應事件或者說處理事件。有兩個子接口:

  • GenericApplicationListener
    接口定義:
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    //相對於ApplicationListener,推展了支持的事件類型
    boolean supportsEventType(ResolvableType eventType);
    boolean supportsSourceType(@Nullable Class<?> sourceType);

}
  • SmartApplicationListener
    雖然叫做Smart,好像很厲害,但是其實已經是過時了的接口,GenericApplicationListener就是其替代者

事件處理過程

以springboot啓動過程中,由SpringApplication發佈的第一個SpringApplicationEvent事件:ApplicationStartingEvent爲例:(對於ac直接發佈事件,一般是ac.publishEvent()方法,但其實內部也就是調用multicastEvent()方法)
SimpleApplicationEventMulticaster發佈事件的代碼如下:

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//1
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {//2
            Executor executor = getTaskExecutor();
            //3
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

1. ResolvableType

是spring裏對java 類型的一種解析,封裝了某個 java.lang.reflect.Type的元數據,比如它的泛型、實現的接口、父類等等

2. getApplicationListeners(event, type)從管理的所有listener中選擇出可以響應這個事件的listener

其具體做法是:

  • 遍歷所有的listener
  • 將所有的listener處理成GenericApplicationListener,對於原來不是GenericApplicationListener的,就使用GenericApplicationListenerAdapter適配成GenericAL
  • 根據GenericAL接口中的supportsEventType和supportsSourceType方法的返回確定改listener是否需要被調用

3. 調用滿足條件的listener

這裏涉及到SimpleApplicationEventMulticaster的兩個屬性:

  • taskExecutor:如果不爲null,則對listener的調用會交由該線程池處理
  • errorHandler:如果不爲null,則對listener調用過程中如果出現異常,會交由該ErrorHandler處理

怎麼在spring中註冊自己的監聽器

方法有3種:

  1. 通過在application.properties中配置context.listener.classes屬性,值是具體的類名,多個值以逗號分隔
  2. 任意實現了ApplicationListener並註冊到ApplicationListener中的bean都會被自動檢測到
  3. 在任意bean的方法上加上@EventListener註解,也可以

怎麼發佈事件

我們知道spring中有個Aware標記接口,也有很多像ApplicationContextAware/BeanFactoryAware/EnvironmentAware/ResourceLoaderAware等這個接口的子接口,只要實現了當中的XXAware接口,在bean實例時,就會通過BeanPostProcessor把對應的XX注入到bean中。在spring中事件發佈能力由ApplicationEventMulticaster 接口定義,所以可以通過讓bean實現ApplicationEventPublisherAware接口,讓bean可以獲取ApplicationEventPublisher的引用,從而具備發佈事件的能力。
其實這些實現XXAware接口的bean,只要這個ApplicationContext接口實現了XX接口,得到的引用其實就是ApplicationContext的實現,所以實現ApplicationEventPublisherAware接口的bean,得到的其實也是ApplicationContext的引用

springboot啓動過程中自動註冊的10個監聽器

1. 怎麼個自動法

其實也沒啥了不得的東西。跟springboot自動裝配的那些bean的機制一樣:

  • 讀取classpath下所有路徑爲META-INF/spring.factories文件
  • 找到當中key爲org.springframework.context.ApplicationListener的所有值
  • 實例化這些value對應的類,並作爲ApplicationListener註冊到ApplicationContext中

2.這10個listener各自的作用

  • BackgroundPreinitializer
    新建後臺進程,進行一些比較耗時的bean的初始化
  • ClearCacheApplicationListener
    context加載完成後清理緩存
  • ParentContextCloserApplicationListener
    當父ApplicationContext(如果有的話)關閉後,也把當前的AC關閉
  • FileEncodingApplicationListener
    spring.mandatory-file-encoding這個屬性的校驗,如果存在的話看他是否和JDK的一致,不一致就報錯
  • AnsiOutputAL
    根據spring.output.ansi.enabled屬性配置AnsiOutput
  • ConfigFileApplicationListener
    這個就厲害了,spring之所以會自動讀取classpath下的application.properties/yml,就是這個listener的實現。默認情況下會從:classpath:/classpath:config/等中加載application.properties/application.yml.也會基於active profile載入額外的文件.路徑或者文件名可以通過spring.config.name/spring.config.location配置
  • DelegatingApplicationListener
    如果通過context.listener.classes屬性往AC中加入了自定義的ApplicationListener,這個DelegatingAL就會把事件傳播給這些自定義的實現
  • ClasspathLoggingApplicationListener
    僅響應ApplicationEnvironmentPreparedEvent/ApplicationFailedEvent,通過以debug級別打印thread context class loader
  • LoggingApplicationListener
    配置LoggingSystem.如果environment中有logging.config配置,則會用於引導LoggingSystem.並且,日誌級別可以通過logging.level.*來配置.默認情況下,僅console會有日誌輸出.如果需要日誌文件,可以通過logging.path或者logging.file來配置
  • LiquibaseServiceLocatorApplicationListener

結語

(水平有限,最近在看spring源碼,分享學習過程,希望對各位有點微小的幫助。
如有錯誤,請指正~)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章