1. 程式人生 > >springboot原始碼分析14-事件釋出機制以及應用監聽器

springboot原始碼分析14-事件釋出機制以及應用監聽器

摘要:事件驅動模型,也就是我們經常提到用到的觀察者模式。當然也可以將其理解為釋出-訂閱模型。具體的實現要素有如下幾個方面。

1、首先是一對多的關係,一是目標物件,多則是觀察者物件。比如報社是一個,而訂報者是多個。

2、當目標物件的行為發生變化的時候,多個觀察者物件會級聯觸發並做出相應的處理。換言之,目標物件的行為發生變化的時候,只需要通知一下所有的觀察者物件(訂閱過的)即可。具體的各個觀察者怎麼去處理,使用什麼方式去處理,並不是目標物件所需要考慮的範疇。也就是說目標與觀察者的實現是低耦合。目標物件的職責就是去通知各個觀察者,各個觀察者的職責是具體做事情的。大家各司其職協調工作。

3、目標物件怎麼能在自身狀態發生變化的時候,去實時通知到各個觀察者呢?無外乎就是如下的兩種思路。

實現方案1:

     所有需要通知的觀察者去目標物件中註冊登記,當目標物件需要通知的時候,查詢登記列表中的所有觀察者,然後一個個的下發。

實現方案2:

所有需要通知的觀察者去目標物件中註冊登記,登記的時候告訴目標物件自己需要監聽的事件型別,只有是自己註冊的事件變化時,才接受通知,否則目標物件的其他事件不要通知這個觀察者。

上述的兩個方案,方案1強調的是目標物件只要發生行為狀態改變,所有的觀察者都可以收到通知,並自行處理。方案2有點類似精準投遞,比如觀察者物件1只監聽a事件,那麼當目標物件觸發b事件的時候不需要通知觀察者物件1。兩種方案各有優缺點,我個人傾向使用方案2。因為該方案可以根據不同的事件源去通知不同的觀察者。

瞭解了上述的內容之後,接下來我們看一下Springboot中所使用的事件釋出機制以及ApplicationListener。

1.1. SpringApplicationRunListener

我們一步到位,直接定位到SpringApplication類中的run方法,相關實現程式碼如下:

public ConfigurableApplicationContext run(String... args) {

...

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

...

}

我們重點看一下

getRunListeners方法的處理邏輯,該方法的例項程式碼如下:

private SpringApplicationRunListeners getRunListeners(String[] args) {

Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };

return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(

pringApplicationRunListener.class, types, this, args));

}

首先看一下getSpringFactoriesInstances方法,程式碼如下:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,

Class<?>[] parameterTypes, Object... args) {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

Set<String> names = new LinkedHashSet<>(

SpringFactoriesLoader.loadFactoryNames(type, classLoader));

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,

classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);//order值越小,越靠前

return instances;

}

上述的程式碼,前面的系列文章也詳細講解過,就是查詢所有META-INF/spring.factories配置檔案中key為org.springframework.boot.SpringApplicationRunListener的值,我們找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置檔案,相關配置如下所示:

# Run Listeners

org.springframework.boot.SpringApplicationRunListener=\

org.springframework.boot.context.event.EventPublishingRunListener

因此上述程式碼執行完畢之後。會通過反射例項化EventPublishingRunListener類,該類的建構函式如下:

private final SimpleApplicationEventMulticaster initialMulticaster;

public EventPublishingRunListener(SpringApplication application, String[] args) {

this.application = application;

this.args = args;

this.initialMulticaster = new SimpleApplicationEventMulticaster();

for (ApplicationListener<?> listener : application.getListeners()) {

this.initialMulticaster.addApplicationListener(listener);

}

}

首先,例項化SimpleApplicationEventMulticaster類,然後呼叫application物件中的getListeners()方法,並迴圈將該函式的返回值集合新增到initialMulticaster中。initialMulticaster我們可以將其理解為事件釋出器。getListeners()方法中返回的集合在哪裡初始化的呢?我們繼續回到SpringApplication類的建構函式中。如下所示:

private List<ApplicationListener<?>> listeners;

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

...

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

}

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {

this.listeners = new ArrayList<>();

this.listeners.addAll(listeners);

}

SpringApplication類的建構函式中,也就直接通過getSpringFactoriesInstances方法直接獲取到META-INF/spring.factories配置檔案中key為org.springframework.context.ApplicationListener的值,我們找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置檔案,相關配置如下所示:

# Application Listeners

org.springframework.context.ApplicationListener=\

org.springframework.boot.ClearCachesApplicationListener,\

org.springframework.boot.builder.ParentContextCloserApplicationListener,\

org.springframework.boot.context.FileEncodingApplicationListener,\

org.springframework.boot.context.config.AnsiOutputApplicationListener,\

org.springframework.boot.context.config.ConfigFileApplicationListener,\

org.springframework.boot.context.config.DelegatingApplicationListener,\

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\

org.springframework.boot.context.logging.LoggingApplicationListener,\

org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

上述的13個監聽類均是監聽不同的事件進行處理的,我們也可以自定義一些監聽器進行業務處理,新增方式如下所示:

SpringApplication springApplication = new SpringApplication(DemoApplication.class);

   springApplication.addListeners(new ShareniuApplicationListener());

通過上述程式碼我們可以看出,所有的事件監聽器最終儲存在SpringApplication類中的listeners集合中。

1.2. Springboot中事件的釋出以及監聽

接下來,我們來看一下Springboot中事件的釋出以及監聽。我們繼續迴歸到listeners.starting()方法中。

public void starting() {

for (SpringApplicationRunListener listener : this.listeners) {

listener.starting();

}

}

迴圈遍歷所有的listeners,並依此呼叫listeners中的starting方法。

注意:listeners是SpringApplicationRunListener型別,並非是ApplicationListener型別,這點一定不要搞混淆了。SpringApplicationRunListener中持有所有的ApplicationListener型別監聽器集合。EventPublishingRunListener類中的starting方法程式碼如下:

public void starting() {

this.initialMulticaster.multicastEvent(

new ApplicationStartingEvent(this.application, this.args));

}

注意這裡產生的事件是ApplicationStartingEvent型別,因此只有監聽到ApplicationStartingEvent事件的監聽器才可以觀察到進而進行自己的處理。this.initialMulticaster.multicastEvent方法實現如下:

public void multicastEvent(ApplicationEvent event) {

multicastEvent(event, resolveDefaultEventType(event));

}

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {

ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {

Executor executor = getTaskExecutor();

if (executor != null) {

executor.execute(() -> invokeListener(listener, event));

}

else {

invokeListener(listener, event);

}

}

}

在構造ApplicationStartedEvent時傳給它的基類EventObjectprotected不可序列化屬性source。例項化ApplicationStartedEventinstance.getClass()幷包裝為ResolvableType型別以儲存型別資訊,並將它和event作為引數傳入SimpleApplicationEventMulticastermulticastEvent方法。multicastEvent首先獲取ApplicationListener,使用getApplicationListeners方法,這個方法中拋開對listener做了一些快取類工作外,主要就是將事件和對應的監聽器做了下是否支援的驗證,返回通過了retrieveApplicationListeners中通過了supportsEvent驗證的監聽器集合,這裡就體現出了ResolvableType的作用,它儲存了型別的資訊同時對泛型型別也支援。

在整個SpringBoot啟動的過程中,會先後出產生如下的幾個事件。

ApplicationStartingEvent-->>ApplicationEnvironmentPreparedEvent-->>ApplicationPreparedEvent

ContextRefreshedEvent-->>ApplicationReadyEvent-->>ContextClosedEvent

後續的系列文章,我們對於核心的監聽器一個個進行講解,從而加深印象。