說到事件監聽,想到的肯定是觀察者模式。但是這兒主要說下spring中的監聽是怎么樣的流程。
這就不得不說到spring容器的refresh方法,容器啟動過程中,listener相關的主要是這個三個方法:initApplicationEventMulticaster方法初始化事件多播器,后續的事件發布都是由多播器來發布的;registerListeners注冊監聽器到前面初始化好的多播器上面去;
finishRefresh容器啟動完成最后刷新,發布ContextRefreshedEvent事件。
1.初始化多播器:獲取bean工廠對象ConfigurableListableBeanFactory,判斷容器中是否有applicationEventMulticaster多播器,如果沒有則創建一個一個簡單事件多播器SimpleApplicationEventMulticaster並注冊到容器中,后續使用
2.注冊監聽器到多播器上並發布早期事件:首先獲取容器中已有的監聽器(成品對象,從第一張圖中可以看到我們自己的組件對象在registerListeners方法調用的時候 還沒有初始化,是在下面的finishBeanFactoryInitialization方法中才進行初始化的),注冊到多播器;然后獲取bean定義中的監聽器,也就是我們自己定義的監聽器;同樣也注冊到多播器上去;最后如果有早期事件就去發布早期事件(multicastEvent方法),這些事件只能由已經實例化的監聽器監聽,我們自己的監聽器初始化是在finishBeanFactoryInitialization方法中。
發布事件:multicastEvent方法----->invokeListener方法---->doInvokeListener方法調用監聽器的onApplicationEvent
可以看到這里支持異步發送,但是從上面我們初始化簡單多播器的時候,executer對象並沒有賦值,因此始終是同步發布。如果我們想實現異步發布事件,那么就要讓上面初始化多播器的邏輯走進第一個分支。我們可以在容器中自己繼承SimpleApplicationEventMulticaster,並初始化一個線程池,然后將其注冊到容器中,bean的名字必須使用“applicationEventMulticaster”,因為此時容器還沒有創建真正的對象,只有這個名字的bean定義才會馬上去創建對象。這樣就可以實現異步發布事件了。
3.執行finishRefresh方法發布ContextRefreshedEvent事件,標志的容器已經啟動完成。
監聽器的流程完了,我們現在來看下使用
首先實現一個自己的監聽器
package com.nijunyang.spring.listener; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * Description: * Created by nijunyang on 2020/2/20 21:53 */ @Component public class MyListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("收到事件:" + event.toString()); } }
新建一個自己的事件:
package com.nijunyang.spring.listener; import org.springframework.context.ApplicationEvent; /** * Description: * Created by nijunyang on 2020/2/20 22:05 */ public class MyApplicationEvent extends ApplicationEvent { /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) */ public MyApplicationEvent(Object source) { super(source); } }
配置類:指定要掃描的包
package com.nijunyang.spring; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author: create by nijunyang * @date:2019/10/6 */ @Configuration @ComponentScan(basePackages = "com.nijunyang.spring.*") public class MainConfig { }
測試代碼:在容器創建完之后發布自己的事件。
package com.nijunyang.spring; import com.nijunyang.spring.listener.MyApplicationEvent; import com.nijunyang.spring.model.Student; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); context.publishEvent(new MyApplicationEvent("想漲工資")); } }
執行代碼會發現我們的監聽器會監聽到兩個事件,因為我們監聽器監聽的事件是ApplicationEvent,上面說到容器啟動的時候,最后會執行finishRefresh方法發布ContextRefreshedEvent事件,容器啟動完成之后,我們自己手動發布了一個我們自己的事件,因此會監聽到兩個事件。
修改我們的監聽器,只監聽我們自己的事件:
package com.nijunyang.spring.listener; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * Description: * Created by nijunyang on 2020/2/20 21:53 */ @Component public class MyListener implements ApplicationListener<MyApplicationEvent> { @Override public void onApplicationEvent(MyApplicationEvent event) { System.out.println("收到事件:" + event.toString()); } }
再次執行代碼,發現現在就只能監聽我們自己的事件了
通過spring的監聽器,我們不僅可以實現自己相關的業務,還可以通過這個機制將我們自己的組件和spring進行整合,比如阿里的nacos就是通過ApplicationListener與spring整合的;
springboot和spring中的一些事件:
ContextClosedEvent:容器關閉的時候,我們可以監聽這個事件在容器關閉的時候去清理一些緩存(比如redis)的數據
ApplicationFailedEvent:該事件為spring boot啟動失敗時的操作
ApplicationPreparedEvent:上下文context准備時觸發
ApplicationReadyEvent:上下文已經准備完畢的時候觸發,做權限認證的時候。在這個時候就可以去初始化一些權限數據。或者預備其他數據
ApplicationEnvironmentPreparedEvent:環境事先准備