Spring事件傳播機制

????Spring是基于事件驅動模型的,事件驅動模型也就是我們常說的觀察者,或者發布-訂閱模型。理解觀察者模式更有助于理解 Spring 事件機制,話不多說,我們先來看一下 Spring 的事件角色的類圖


Spring 的事件角色的類圖.png

????從此類圖中我們可以得到以下信息:
????1.事件源:如果我們需要實現事件傳播的話,我們首先需要實現自己的事件類去實現 ApplicationEvent 接口。
????2.監聽者:需要定義自己的事件監聽器類去實現 ApplicationListener<E extends ApplicationEvent> 接口。
????3.事件發布:需要有一個對象去發布該事件,從類圖中我們可以了解到,應用上下文 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());
    }
}

異步事件:
  上面的監聽事件都是同步觸發的,如果想異步只需要兩步:

  1. 啟動類上添加 @EnableAsync注解,開啟異步支持。
  2. 監聽方法上添加 @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();
            }
        }
}

????直接來看 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事件的基本處理流程。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。