Spring是基於事件驅動模型的,事件驅動模型也就是我們常說的觀察者,或者發布-訂閱模型。理解觀察者模式更有助於理解 Spring 事件機制,話不多說,我們先來看一下 Spring 的事件角色的類圖
從此類圖中我們可以得到以下信息:
- 事件源:如果我們需要實現事件傳播的話,我們首先需要實現自己的事件類去實現 ApplicationEvent 接口。
- 監聽者:需要定義自己的事件監聽器類去實現 ApplicationListener<E extends ApplicationEvent> 接口。
- 事件發布:需要有一個對象去發布該事件,從類圖中我們可以了解到,應用上下文 ApplicationContext 就是一個天生的事件發布源。我們通常可以 事件ApplicationEventPublisherAware接口來注入上下文中的 ApplicationEventPublisher,既可以通過它發布事件
需要知道的是,在spring源碼中 AbstractApplicationContext 持有了 ApplicationEventMulticaster引用,且通過他進行事件的發布,默認實現為 SimpleApplicationEventMulticaster。那么了解了這些,我們可以來簡單實現一下我們的spring自定義事件:
1.定義一個類,及源事件:
public class Order { private String orderNo; private String orderStatus; private String goods; private Date createTime; //省略 get/set
} //源事件
public class OrderCreateEvent extends ApplicationEvent { private final Order order; public OrderCreateEvent(Object source, Order order) { super(source); this.order = order; } public Order getOrder() { return order; } }
2.定義事件監聽器:
@Component public class OrderCreateEventListener implements ApplicationListener<OrderCreateEvent> { @Override public void onApplicationEvent(OrderCreateEvent event) { System.out.printf(this.getClass().getName() + " -- ApplicationListener 接口實現,訂單號[%s]:,商品[%s]\n", event.getOrder().getOrderNo(), event.getOrder().getGoods()); } }
3.定義事件發服務:
@Service public class OrderService implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; /** * 訂單保存 */
public void save(){ Order order = new Order(); order.setOrderNo("1"); order.setGoods("手機"); System.out.println("訂單保存成功:" + order.toString()); //發布事件
applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order)); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } }
4.編寫測試類:
@RunWith(SpringRunner.class) @SpringBootTest(classes = SpringStudyApp.class) public class SpringStudyTest { @Autowired OrderService orderService; @Test public void contextLoads() { orderService.save(); } }
簡單的幾步就實現了自定義的spring事件。
基於注解的事件監聽@EventListener:
@Component public class OrderCreateEventListenerAnnotation { @EventListener public void createOrderEvent(OrderCreateEvent event) { System.out.println(this.getClass().getName() + "--訂單創建事件,@EventListener注解實現,orderNo:" + event.getOrder().getOrderNo()); } }
異步事件:
上面的監聽事件都是同步觸發的,如果想異步只需要兩步:
- 啟動類上添加 @EnableAsync注解,開啟異步支持。
- 監聽方法上添加 @Async注解
事件傳播機制:
當我們監聽一個事件處理完成時,還需要發布另一個事件,一般我們想到的是調用ApplicationEventPublisher#publishEvent發布事件方法,但Spring提供了另一種更加靈活的新的事件繼續傳播機制,監聽方法返回一個事件,也就是方法的返回值就是一個事件對象
@Component public class OrderListener { @EventListener public void orderListener(Order order){ System.out.println(this.getClass().getName() + " -- 監聽一個訂單"); } //事件傳播機制 // 當我們監聽一個事件處理完成時,還需要發布另一個事件,一般我們想到的是調用ApplicationEventPublisher#publishEvent發布事件方法, // 但Spring提供了另一種更加靈活的新的事件繼續傳播機制,監聽方法返回一個事件,也就是方法的返回值就是一個事件對象。
@EventListener public OrderCreateEvent orderReturnEvent(Order order){ System.out.println(this.getClass().getName() + " -- 監聽一個訂單,返回一個新的事件 OrderCreateEvent"); return new OrderCreateEvent(this,order); } }
這樣子我們后續通過 applicationEventPublisher.publishEvent(order); 發布一個 Order 類型的參數的事件,他會被上面的 orderReturnEvent 方法監聽到,然后最后返回一個 OrderCreateEvent 事件對象,繼而觸發該事件的監聽。
基於事件驅動模型可以很方便的實現解耦,提高代碼的可讀性和可維護性,那么 Spring中是怎么進行初始化及使用的呢?我們來一探究竟。
Spring 事件源碼分析:
我們知道 Spring 容器的初始化都會走到 AbstractApplicationContext 的 refresh() 方法:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // 關鍵來了,為此上下文初始化事件多播程序 // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // 調用子類特殊的某些特殊的方法刷新容器 // Initialize other special beans in specific context subclasses.
onRefresh(); // 檢查偵聽器bean並注冊它們。 // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh(); } } }
其他的方法我們在 IOC容器初始化 一文中已經介紹過了,這里直接來看 initApplicationEventMulticaster() 方法:
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
// 獲取上面初始化的 DefaultListableBeanFactory 容器 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 如果容器里找到了這個類,那么直接賦值給成員變量 this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isDebugEnabled()) { logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } else {// 初始化一個 SimpleApplicationEventMulticaster this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isDebugEnabled()) { logger.debug("Unable to locate ApplicationEventMulticaster with name '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]"); } } }
其實這個方法主要的作用是保證容器中又一個被實例化的ApplicationEventMulticaster 類型的Bean。緊接着等待子類特殊的刷新方法結束基本上整個容器也就初始化完成了。然后調用 registerListeners():
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
//注冊監聽器
protected void registerListeners() { // Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
// 遍歷上面這個集合,將提前儲備好的監聽器添加到監聽器容器中 getApplicationEventMulticaster().addApplicationListener(listener); } // 獲得容器中類型是 ApplicationListener 的 beanName集合 // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // 發布這個 earlyApplicationEvents 容器中的事件 // Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
// 委派上面初始化的 SimpleApplicationEventMulticaster 進行廣播發布 getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
緊接着來看 multicastEvent(ApplicationEvent event):
@Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { // 再去通過event去獲得一個ResolvableType
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 通過事件及類型進行獲取事件監聽器進行遍歷
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); // 當 executor 不為空,這里走的是異步事件
if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } // 同步事件
else { invokeListener(listener, event); } } }
從這里去調用 listener 的 onApplicationEvent 方法:
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } //.....省略部分代碼
}
像容器初始化完成,AbstractApplicationContext的 finishRefresh() 方法中
protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches(); // Initialize lifecycle processor for this context.
initLifecycleProcessor(); // Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh(); // 發布容器刷新完成事件 // Publish the final event.
publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this); }
這里所調用的 publishEvent(new ContextRefreshedEvent(this)) 最終還是走 multicastEvent(ApplicationEvent event) 方法的。這就是Spring事件的基本處理流程。