javascript
Spring IoC 源码系列(三)Spring 事件发布机制原理分析
在 IoC 容器啟動流程中有一個 finishRefresh 方法,具體實現如下:
protected void finishRefresh() {clearResourceCaches();initLifecycleProcessor();getLifecycleProcessor().onRefresh();// 向所有監聽 ContextRefreshedEvent 事件的監聽者發布事件publishEvent(new ContextRefreshedEvent(this));LiveBeansView.registerApplicationContext(this);}這里我們只關注 publishEvent 方法,這個方法用于發布 IoC 刷新完成事件,事件時如何發布的呢?下面我們一起來看一下。
一、原理分析
@Overridepublic void publishEvent(ApplicationEvent event) {publishEvent(event, null);}protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");// Decorate event as an ApplicationEvent if necessaryApplicationEvent 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 initializedif (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);} else {// 獲取 ApplicationEventMulticaster,將事件廣播出去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);}}}通過上面方法可以看出 Spring 的事件是通過 ApplicationEventMulticaster 廣播出去的,這個 ApplicationEventMulticaster 在 IoC 啟動流程 initApplicationEventMulticaster 方法中初始化。如果該容器還存在父容器,那也會以同樣的形式將事件發布給父容器的監聽者。
@Overridepublic 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) {// 如果執行器部位 null,則異步執行將事件發布給每一個監聽者executor.execute(() -> invokeListener(listener, event));}else {// 同步發布事件invokeListener(listener, event);}}}發布事件前會先獲取所有已注冊的監聽器,而監聽器早已在 IoC 啟動流程的 registerListeners 方法中注冊。獲取到所有事件監聽器之后,就可以進行事件發布了。發布的時候分為異步執行與順序執行,默認情況下 executor 是沒有初始化的,因此是順序執行。
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;}}}到這里 Spring 的事件通知機制流程就結束了,總的來說還是比較好理解的。
二、事件通知 demo
了解了事件通知機制的基本原理后,下面我們來寫個 demo 體驗一下監聽器是如何使用的。參考自:Event事件通知機制
// 定義一個 Event public class EventDemo extends ApplicationEvent {private static final long serialVersionUID = -8363050754445002832L;private String message;public EventDemo(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;} }// 定義一個監聽器1 public class EventDemo1Listener implements ApplicationListener<EventDemo> {public void onApplicationEvent(EventDemo event) {System.out.println(this + " receiver " + event.getMessage());} }// 定義一個監聽器2 public class EventDemo2Listener implements ApplicationListener<EventDemo> {public void onApplicationEvent(EventDemo event) {System.out.println(this + " receiver " + event.getMessage());} }// 定義一個事件發布者 public class EventDemoPublish {public void publish(ApplicationEventPublisher applicationEventPublisher, String message) {EventDemo eventDemo = new EventDemo(this, message);applicationEventPublisher.publishEvent(eventDemo);} }在 XML 中配置 bean:
<bean id="eventDemoPublish" class="com.jas.mess.event.EventDemoPublish"/><bean id="eventDemo1Listener" class="com.jas.mess.event.EventDemo1Listener"/><bean id="eventDemo2Listener" class="com.jas.mess.event.EventDemo2Listener"/>編寫測試類:
@Testpublic void eventTest() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);applicationContext.getBean("eventDemoPublish", EventDemoPublish.class).publish(applicationContext, "hello world");}控制臺輸出:
不知道你有沒有注意到,在發布事件的時候我們傳的發布者是 applicationContext,applicationContext 本身繼承自 ApplicationEventPublisher 接口,因此它本身也是一個事件發布者。
參考閱讀
Event事件通知機制 by wangqi
總結
以上是生活随笔為你收集整理的Spring IoC 源码系列(三)Spring 事件发布机制原理分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天使轮的股份上限 不宜超过30%
- 下一篇: 退市金玉是做什么的