摘要:事件驅動模型,也就是我們經常提到用到的觀察者模式。當然也可以将其了解為釋出-訂閱模型。具體的實作要素有如下幾個方面。
1、首先是一對多的關系,一是目标對象,多則是觀察者對象。比如報社是一個,而訂報者是多個。
2、當目标對象的行為發生變化的時候,多個觀察者對象會級聯觸發并做出相應的處理。換言之,目标對象的行為發生變化的時候,隻需要通知一下所有的觀察者對象(訂閱過的)即可。具體的各個觀察者怎麼去處理,使用什麼方式去處理,并不是目标對象所需要考慮的範疇。也就是說目标與觀察者的實作是低耦合。目标對象的職責就是去通知各個觀察者,各個觀察者的職責是具體做事情的。大家各司其職協調工作。
3、目标對象怎麼能在自身狀态發生變化的時候,去實時通知到各個觀察者呢?無外乎就是如下的兩種思路。
實作方案1:
所有需要通知的觀察者去目标對象中注冊登記,當目标對象需要通知的時候,查詢登記清單中的所有觀察者,然後一個個的下發。
實作方案2:
所有需要通知的觀察者去目标對象中注冊登記,登記的時候告訴目标對象自己需要監聽的事件類型,隻有是自己注冊的事件變化時,才接受通知,否則目标對象的其他事件不要通知這個觀察者。
上述的兩個方案,方案1強調的是目标對象隻要發生行為狀态改變,所有的觀察者都可以收到通知,并自行處理。方案2有點類似精準投遞,比如觀察者對象1隻監聽a事件,那麼當目标對象觸發b事件的時候不需要通知觀察者對象1。兩種方案各有優缺點,我個人傾向使用方案2。因為該方案可以根據不同的事件源去通知不同的觀察者。
了解了上述的内容之後,接下來我們看一下Springboot中所使用的事件釋出機制以及ApplicationListener。
我們一步到位,直接定位到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集合中。
接下來,我們來看一下Springboot中事件的釋出以及監聽。我們繼續回歸到listeners.starting()方法中。
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
循環周遊所有的listeners,并依此調用listeners中的starting方法。
注意:listeners是SpringApplicationRunListener類型,并非是ApplicationListener類型,這點一定不要搞混淆了。SpringApplicationRunListener中持有所有的ApplicationListener類型監聽器集合。EventPublishingRunListener類中的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時傳給它的基類EventObject的protected不可序列化屬性source。執行個體化ApplicationStartedEvent後instance.getClass()并包裝為ResolvableType類型以儲存類型資訊,并将它和event作為參數傳入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先擷取ApplicationListener,使用getApplicationListeners方法,這個方法中抛開對listener做了一些緩存類工作外,主要就是将事件和對應的監聽器做了下是否支援的驗證,傳回通過了retrieveApplicationListeners中通過了supportsEvent驗證的監聽器集合,這裡就展現出了ResolvableType的作用,它儲存了類型的資訊同時對泛型類型也支援。
在整個SpringBoot啟動的過程中,會先後出産生如下的幾個事件。
ApplicationStartingEvent-->>ApplicationEnvironmentPreparedEvent-->>ApplicationPreparedEvent
ContextRefreshedEvent-->>ApplicationReadyEvent-->>ContextClosedEvent
後續的系列文章,我們對于核心的監聽器一個個進行講解,進而加深印象。