深入理解Spring事件機制(二):事件的傳播

前言

Spring 從 3.x 開始支持事件機制。在 Spring 的事件機制中,我們可以令一個事件類繼承 ApplicationEvent 類,然後將實現了 ApplicationListenerBean 註冊到 spring 容器,最後向 ApplicationEventPublisher 推送事件對象即可令所有訂閱者收到事件。在 4.2 以後,甚至不需要實現 ApplicationListener 接口,僅需在 Bean 中方法標記 @EventListener 註解即可。

筆者將基於 Spring 源碼的 5.2.x 分支,分析該功能是如何實現的。

本文是其中的第二篇文章,將分析事件是如何通過廣播器推送,並被監聽器接收並處理的。

在開始前,推薦先閱讀前文了解一點 Spring 的註解機制或者事務機制,這將更有利於流程與一些代碼的理解。

相關文章:

一、事件的推送

1、將事件推送到上下文

當我們藉助 Spring 發送一個事件對象的時候,一般都通過 ApplicationEventPublisher 完成,在默認情況下,通過容器獲得的 ApplicationEventPublisher 單例實際上就是 ApplicationContext 本身。

ApplicationEventPublisher 提供了兩個 publishEvent 方法,一個用於發佈 ApplicationEvent 事件,另一個用於發佈其他事件,在 AbstractApplicationContext 中,它們都通過同一個私有方法實現:

// 推送ApplicationEvent事件
@Override
public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}
// 推送非ApplicationEvent事件
@Override
public void publishEvent(Object event) {
    publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");

    // 如果事件對象沒繼承ApplicationEvent,就包裝爲PayloadApplicationEvent
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            // 獲得事件對象的實際類型
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }

    // 如果上下文中早期事件列表的事件沒清空,說明還在上下文初始化過程,還沒有可用的廣播器
    // 因此此時不直接廣播事件,而是加入早期事件列表,等到廣播器完成初始化後再推送
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        // 直接推送事件
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // 如果有父上下文,則也向父上下文推送事件
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

這一步方法總共做了這些事:

  • 如果事件對象沒有繼承 ApplicationEvent,則將其包裝爲 PayloadApplicationEvent
  • 若早期事件列表爲空,說明還在上下文已有可用的廣播器,直接通過廣播器推送事件,否則就先把事件加入早期事件列表,等到廣播器初始化完成後再推送;
  • 如果上下文存在父上下文,則向父上下文也推送事件;

針對早期事件列表,在容器調用 AbstractApplicationContext.refresh 方法進行初始化的過程中,早期事件列表在整個容器啓動的第一個步驟 prepareRefresh 中被創建,而在非常靠後的 registerListeners 步驟中才被清空。

也就是說,當 registerListeners 還沒執行前,任何向上下文推送的事件實際上都不會立刻執行,而是延遲到 registerListeners 這一步纔會推送,在這一步後,向上下文推送的事件都會立刻被推送。

2、將事件推送到廣播器

當上下文將事件推送到廣播器時,需要調用 ApplicationEventMulticaster.multicastEvent 方法,我們以默認的實現類 SimpleApplicationEventMulticaster 爲例:

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    // 獲取事件的實際類型
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 獲取任務執行器,
    Executor executor = getTaskExecutor();
    // 獲取已經註冊的監聽器
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            // 通過執行器執行廣播調用過程
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

這一步共幹了四件事:

  • 獲取事件的實際類型;
  • 獲取廣播器中配置的任務執行器;
  • 通過事件的實際類型獲取對應的監聽器;
  • 遍歷監聽器,在執行器中調用監聽器;

更簡單的概況,就是:找到事件對應的監聽器,然後依次放到執行器執行。

下面我們將詳細分析監聽器查詢與執行的過程。

二、監聽器的檢索

在廣播器廣播事件時,會調用 getApplicationListeners 方法:

protected Collection<ApplicationListener<?>> getApplicationListeners(
    ApplicationEvent event, ResolvableType eventType) {

    // 獲取事件的實際類型
    Object source = event.getSource();
    Class<?> sourceType = (source != null ? source.getClass() : null);
    // 生成監聽器與事件類型的緩存key
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

    // 從緩存中獲取事件對應的監聽器檢索器,若不存在則新建並加入緩存
    CachedListenerRetriever newRetriever = null;
    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    if (existingRetriever == null) {
        // Caching a new ListenerRetriever if possible
        if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
             (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            newRetriever = new CachedListenerRetriever();
            existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
            if (existingRetriever != null) {
                newRetriever = null;  // no need to populate it in retrieveApplicationListeners
            }
        }
    }

    // 若存在,則從檢索器中獲取全部監聽器
    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
        // If result is null, the existing retriever is not fully populated yet by another thread.
        // Proceed like caching wasn't possible for this current local attempt.
    }

    // 從檢索器中獲取所需的監聽器
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

1、監聽器檢索器

默認的廣播器 SimpleApplicationEventMulticaster 中維護了兩個檢索器內部類,用於管理註冊到廣播器的監聽器,它們有不同的用途:

  • DefaultListenerRetriever :用於支持監聽器的註冊功能;
  • CachedListenerRetriever:用於提供基於事件類型快速查詢對應監聽器的緩存功能;

DefaultListenerRetriever

DefaultListenerRetriever 主要用於支持監聽器的註冊,他唯一的作用就是提供 getApplicationListeners 方法:

private class DefaultListenerRetriever {

    // 監聽器實例
    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

    // 監聽器Bean名稱
    public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

    public Collection<ApplicationListener<?>> getApplicationListeners() {
        // 先獲得Bean實例
        List<ApplicationListener<?>> allListeners = new ArrayList<>(
            this.applicationListeners.size() + this.applicationListenerBeans.size());
        allListeners.addAll(this.applicationListeners);
        
        // 再根據BeanName從BeanFactory中獲取監聽器Bean
        if (!this.applicationListenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : this.applicationListenerBeans) {
                try {
                    ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener)) {
                        allListeners.add(listener);
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
                }
            }
        }
        // 對監聽器排序
        AnnotationAwareOrderComparator.sort(allListeners);
        return allListeners;
    }
}

該檢索器支持直接註冊 Bean 實例,或者只註冊 BeanName,當調用 getApplicationListeners 將會全量的獲得已註冊的監聽器實例。

CachedListenerRetriever

CachedListenerRetriever 用於提供基於事件類型快速查詢對應監聽器的緩存功能,它總是在 DefaultListenerRetriever 創建後,向廣播器推送事件的時候纔會被創建,而當向廣播器註冊監聽器時,如果已有該緩存檢索器,則它們全部都會被銷燬,等待下一次推送時間時再被創建,從而實現刷新緩存的功能:

private class CachedListenerRetriever {

    @Nullable
    public volatile Set<ApplicationListener<?>> applicationListeners;

    @Nullable
    public volatile Set<String> applicationListenerBeans;

    @Nullable
    public Collection<ApplicationListener<?>> getApplicationListeners() {
        Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;
        Set<String> applicationListenerBeans = this.applicationListenerBeans;
        if (applicationListeners == null || applicationListenerBeans == null) {
            // Not fully populated yet
            return null;
        }

        List<ApplicationListener<?>> allListeners = new ArrayList<>(
            applicationListeners.size() + applicationListenerBeans.size());
        allListeners.addAll(applicationListeners);
        if (!applicationListenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : applicationListenerBeans) {
                try {
                    allListeners.add(beanFactory.getBean(listenerBeanName, ApplicationListener.class));
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
                }
            }
        }
        if (!applicationListenerBeans.isEmpty()) {
            // 對監聽器排序
            AnnotationAwareOrderComparator.sort(allListeners);
        }
        return allListeners;
    }
}

而每一個 CachedListenerRetriever 都會有一個對應的 ListenerCacheKey,這個類重寫了 hashCodeequals 方法,用來在 Map 集合中根據事件類型與事件 source 作爲 key:

private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> {
    private final ResolvableType eventType;
    @Nullable
    private final Class<?> sourceType;
}

2、獲取監聽器

getApplicationListeners 方法中,主要用於創建一個 ListenerCacheKey 對應的 CachedListenerRetriever 緩存實例,不過實際上此時 CachedListenerRetriever 仍然還是空的,需要在最後通過 retrieveApplicationListeners 方法從 DefaultListenerRetriever 中找到對應的監聽器,然後將他們都刷進緩存:

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
    ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
    Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.defaultRetriever) {
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 遍歷監聽器實例,如果支持處理該事件
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            // 有檢索器緩存
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            // 無檢索器緩存
            allListeners.add(listener);
        }
    }

    // 遍歷僅註冊了BeanName的監聽器,並將其中支持處理該事件的監聽器BeanFactory中取出
    if (!listenerBeans.isEmpty()) {
        ConfigurableBeanFactory beanFactory = getBeanFactory();
        for (String listenerBeanName : listenerBeans) {
            try {
                if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                    ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            // 如果是單例Bean,可以直接取出備用
                            if (beanFactory.isSingleton(listenerBeanName)) {
                                filteredListeners.add(listener);
                            }
                            // 如果不是單例Bean,則不保證後續會不會再調整,因此只記錄BeanName,等要用的時候再獲取
                            else {
                                filteredListenerBeans.add(listenerBeanName);
                            }
                        }
                        allListeners.add(listener);
                    }
                }
                else {
                    // 移除不匹配該事件的監聽器
                    Object listener = beanFactory.getSingleton(listenerBeanName);
                    if (retriever != null) {
                        filteredListeners.remove(listener);
                    }
                    allListeners.remove(listener);
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Singleton listener instance (without backing bean definition) disappeared -
                // probably in the middle of the destruction phase
            }
        }
    }

    // 對監聽器排序
    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null) {
        if (filteredListenerBeans.isEmpty()) {
            retriever.applicationListeners = new LinkedHashSet<>(allListeners);
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
        else {
            retriever.applicationListeners = filteredListeners;
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
    }
    return allListeners;
}

而這裏多次調用的 support 方法按順序依次則有:

protected boolean supportsEvent(
    ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

    GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                                                (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
    return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

private boolean supportsEvent(
    ConfigurableBeanFactory beanFactory, String listenerBeanName, ResolvableType eventType) {

    // 獲取監聽器類型,並判斷監聽器是否支持處理該事件:
    Class<?> listenerType = beanFactory.getType(listenerBeanName);
    // 1.GenericApplicationListener與SmartApplicationListener默認支持所有ApplicationEvent,
    // 因此如果監聽器是上述任意一種,直接返回ture
    if (listenerType == null || GenericApplicationListener.class.isAssignableFrom(listenerType) ||
        SmartApplicationListener.class.isAssignableFrom(listenerType)) {
        return true;
    }
    // 2.獲取監聽器ApplicationListener的類泛型,判斷是否爲該類事件,不是就直擊返回false
    if (!supportsEvent(listenerType, eventType)) {
        return false;
    }
    try {
        // 3.如果還不行,嘗試直接從BeanFactory獲取BeanDefinition信息,然後轉爲ApplicationListener後再獲取它在類泛型上聲明的事件類型
        BeanDefinition bd = beanFactory.getMergedBeanDefinition(listenerBeanName);
        ResolvableType genericEventType = bd.getResolvableType().as(ApplicationListener.class).getGeneric();
        // 如果能獲得事件類型就判斷,不能說明實際上支持所有類型,都返回ture
        return (genericEventType == ResolvableType.NONE || genericEventType.isAssignableFrom(eventType));
    }
    catch (NoSuchBeanDefinitionException ex) {
        // Ignore - no need to check resolvable type for manually registered singleton
        return true;
    }
}

protected boolean supportsEvent(Class<?> listenerType, ResolvableType eventType) {
    // 獲取在監聽器泛型聲明的事件類型,確認這個事件類型是否與當前事件爲一類事件
    ResolvableType declaredEventType = GenericApplicationListenerAdapter.resolveDeclaredEventType(listenerType);
    return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));
}

static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
    ResolvableType eventType = eventTypeCache.get(listenerType);
    if (eventType == null) {
        // 將監聽器轉爲ApplicationListener,獲取其類泛型聲明的事件類型
        eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();
        eventTypeCache.put(listenerType, eventType);
    }
    return (eventType != ResolvableType.NONE ? eventType : null);
}

這裏的邏輯有點長,不過主要目標還是很清晰的:

  • 先根據事件的類型和 source 獲取 CachedListenerRetriever 緩存,如果沒緩存就先創建;
  • DefaultListenerRetriever 獲取全部監聽器:
    1. 如果能獲取實例,就直接獲得實例;
    2. 不能獲得實例,就通過 BeanNameBeanFactory 裏取;
  • 判斷這些監聽器支不支持處理當前推送的事件:
    1. 如果能拿到實例,並且如果實現了 GenericApplicationListener,就直接調用 supportsEventTypesupportsSourceType 方法判斷;
    2. 如果不能拿到實例,那就嘗試拿到實際的類型,並且通過泛型確認是否支持當前推送的事件;
  • 獲得全部支持該事件的監聽器後,再將其刷入 CachedListenerRetriever 緩存,下次再來就直接從緩存裏頭取;

三、監聽器的執行

監聽器的執行分爲兩步,第一步是在廣播器中調用監聽器的 onApplicationEvent,第二步是在 onApplicationEvent 調用真正的處理邏輯,這裏根據監聽器類型的不同而具有不同的實現,這裏我們重點關注註解式監聽器。

1、廣播器的廣播

監聽器的執行對應 multicastEvent 中的這一段代碼:

// 獲取任務執行器,
Executor executor = getTaskExecutor();
// 獲取已經註冊的監聽器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    if (executor != null) {
        // 通過執行器異步執行廣播調用過程
        executor.execute(() -> invokeListener(listener, event));
    }
    else {
        // 如果通過當前線程執行廣播調用過程
        invokeListener(listener, event);
    }
}

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;
        }
    }
}

這裏的邏輯非常簡單:

  • 獲取全部支持處理該事件的監聽器,然後依次調用 ApplicationListener.onApplicationEvent 方法;
  • 如果廣播器有設置線程池,則調用監聽器的時候就在線程池裏調用,否則在當前線程裏調用;
  • 當調用發生異常時,就調用廣播器中註冊的異常處理器 ErrorHandler

2、普通監聽器的執行

我們以最基本的註解式監聽器 ApplicationListenerMethodAdapter 爲例,當調用它的 onApplicationEvent 方法時:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    processEvent(event);
}

public void processEvent(ApplicationEvent event) {
    Object[] args = resolveArguments(event);
    if (shouldHandle(event, args)) {
        Object result = doInvoke(args);
        if (result != null) {
            handleResult(result);
        }
        else {
            logger.trace("No result object given - no result to handle");
        }
    }
}

條件判斷

此過程平平無奇,其中 shouldHandle 方法用於根據在 @EventListener 中的 condition 屬性指定的 SpEL 表達式確定是否需要真正調用註解方法:

private boolean shouldHandle(ApplicationEvent event, @Nullable Object[] args) {
    if (args == null) {
        return false;
    }
    String condition = getCondition();
    if (StringUtils.hasText(condition)) {
        Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
        return this.evaluator.condition(
            condition, event, this.targetMethod, this.methodKey, args, this.applicationContext);
    }
    return true;
}

至於 evaluator 則是 Spring 基於 SpEL 表達式封裝的一個用於事件的工具類 EventExpressionEvaluator,關於這個可以單獨瞭解 SpelExpressionParserStandardEvaluationContextExpression 這三個類,或者直接瞭解 Spring 的 SpEL 表達式相關功能。

3、返回值支持

相比於普通的編程式監聽器,註解式監聽器還會多處一步對返回值的處理。我們以 ApplicationListenerMethodAdapter 爲例,當 doInvoke 以後,若註解方法返回值不爲null,則會嘗試通過 handleResult 對返回值進行處理:

protected void handleResult(Object result) {
    if (reactiveStreamsPresent && new ReactiveResultHandler().subscribeToPublisher(result)) {
        if (logger.isTraceEnabled()) {
            logger.trace("Adapted to reactive result: " + result);
        }
    }
    // 1、返回值是CompletionStage
    else if (result instanceof CompletionStage) {
        ((CompletionStage<?>) result).whenComplete((event, ex) -> {
            // 發生異常
            if (ex != null) {
                handleAsyncError(ex);
            }
            // 當完成後若有返回值,則繼續嘗試處理返回值
            else if (event != null) {
                publishEvent(event);
            }
        });
    }
    // 2、返回值是ListenableFuture
    else if (result instanceof ListenableFuture) {
        ((ListenableFuture<?>) result).addCallback(this::publishEvents, this::handleAsyncError);
    }
    // 3、返回值是普通對象、數組或者集合
    else {
        publishEvents(result);
    }
}

當監聽器方法有返回值的時候,這裏有三種處理:

  • 返回值是 CompletionStage,繼續完成下一步異步調用;
  • 返回值是 ListenableFuture,調用回調方法;
  • 返回值返回值是對象、數組或集合,嘗試作爲將其作爲事件對象發送;

返回值是CompletionStage

當看到了 CompletionStage 的時候,很容易聯想到基於它實現的 CompletableFuture。它表示一個待完成的異步任務,在 ApplicationListenerMethodAdapter 中,監聽器會通過如下代碼,爲其追加任務完成後的回調:

// 當這一步完成後,獲取執行結構
((CompletionStage<?>) result).whenComplete((event, ex) -> {
    // 如果發生異常
    if (ex != null) {
        handleAsyncError(ex);
    }
    // 當完成後若有返回值,則繼續嘗試處理返回值
    else if (event != null) {
        publishEvent(event);
    }
});

返回值是ListenableFuture

ListenableFuture 也是一個異步任務回調接口,它的用法與 Guava 中的 ListenableFuture 幾乎完全一致,處理的邏輯與上文的返回值是 CompletionStage 的情況也完全一致,就是追加任務完成後的回調:

((ListenableFuture<?>) result).addCallback(this::publishEvents, this::handleAsyncError);

當任務的返回值不爲 null 的時候,就嘗試處理它的返回值。

返回值是普通對象、數組或集合

當對象是普通對象的時候,監聽器會嘗試將返回值也作爲一個事件推送。而如果是數組或者集合,會先將其攤平,然後把其中的每一個元素都取出嘗試作爲事件推送:

private void publishEvents(Object result) {
    // 如果是數組,就將數組取出然後依次作爲事件發送
    if (result.getClass().isArray()) {
        Object[] events = ObjectUtils.toObjectArray(result);
        for (Object event : events) {
            publishEvent(event);
        }
    }
    // 如果是集合,也平鋪後依次將其中元素作爲事件發送
    else if (result instanceof Collection<?>) {
        Collection<?> events = (Collection<?>) result;
        for (Object event : events) {
            publishEvent(event);
        }
    }
    // 直接將返回值作爲事件發送
    else {
        publishEvent(result);
    }
}

private void publishEvent(@Nullable Object event) {
    if (event != null) {
        Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
        this.applicationContext.publishEvent(event);
    }
}

4、事務支持

由於編程式監聽器天生就是支持事務的,因此 Spring 只單純爲註解式監聽器專門準備的事務監聽器方法適配器 ApplicationListenerMethodTransactionalAdapter,它由監聽器工廠 TransactionalEventListenerFactory 生產,與一個 @TransactionalEventListener 註解對應。

@TransactionalEventListener

@TransactionalEventListener 是一個基於 Spring 元註解機制擴展的註解,它在 @EventListener 的基礎上擴展了一些事務相關的配置:

@EventListener
public @interface TransactionalEventListener {
    
    // 噹噹前事務進行到哪個階段時,調用該監聽器,若fallbackExecution爲true時無意義
    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
    
    // 當前沒有事務的時候,是否調用該監聽器
    boolean fallbackExecution() default false;
    
    // 監聽的事件類型
    @AliasFor(annotation = EventListener.class, attribute = "classes")
    Class<?>[] value() default {};
    @AliasFor(annotation = EventListener.class, attribute = "classes")
    Class<?>[] classes() default {};
    
    // 用於判斷是否處理事件的SpEL表達式
    String condition() default "";

}

ApplicationListenerMethodTransactionalAdapter

事務監聽器方法適配器 ApplicationListenerMethodTransactionalAdapter 繼承了非事務的適配器 ApplicationListenerMethodAdapter,但是基於 TransactionSynchronization 在監聽器的 onApplicationEvent 方法做了一些事務的處理:

class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter {

    private final TransactionalEventListener annotation;


    public ApplicationListenerMethodTransactionalAdapter(String beanName, Class<?> targetClass, Method method) {
        super(beanName, targetClass, method);
        TransactionalEventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class);
        if (ann == null) {
            throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method);
        }
        this.annotation = ann;
    }


    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        // 如果全局事務管理器可以用,則創建一個TransactionSynchronizationEventAdapter同步事務,
        // 並註冊到全局事務管理器
        if (TransactionSynchronizationManager.isSynchronizationActive() &&
            TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);
            TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
        }
        // 如果全局事務管理器不可用,並且在註解中配置了fallbackExecution爲true,即沒有事務也調用監聽器
        else if (this.annotation.fallbackExecution()) {
            if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
                logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
            }
            // 處理器事件
            processEvent(event);
        }
        else {
            // No transactional event execution at all
            if (logger.isDebugEnabled()) {
                logger.debug("No transaction is active - skipping " + event);
            }
        }
    }

    private TransactionSynchronization createTransactionSynchronization(ApplicationEvent event) {
        return new TransactionSynchronizationEventAdapter(this, event, this.annotation.phase());
    }


    private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {

        private final ApplicationListenerMethodAdapter listener;

        private final ApplicationEvent event;

        private final TransactionPhase phase;

        public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener,
                                                      ApplicationEvent event, TransactionPhase phase) {

            this.listener = listener;
            this.event = event;
            this.phase = phase;
        }

        @Override
        public int getOrder() {
            return this.listener.getOrder();
        }

        @Override
        public void beforeCommit(boolean readOnly) {
            // 如果註解中配置了調用階段爲TransactionPhase.BEFORE_COMMIT,則事務提交前調用
            if (this.phase == TransactionPhase.BEFORE_COMMIT) {
                processEvent();
            }
        }

        @Override
        public void afterCompletion(int status) {
            // 事務提交時調用
            if (this.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {
                processEvent();
            }
            // 事務回滾時調用
            else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
                processEvent();
            }
            // 事務提交完畢後調用
            else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
                processEvent();
            }
        }

        protected void processEvent() {
            this.listener.processEvent(this.event);
        }
    }

}

這裏的邏輯也很直白:

  • 當調用方法時 onApplicationEvent 方法時,判斷事務是否可用;
  • 事務不可用,並且 @TransactionalEventListenerfallbackExecution 屬性爲 true,則直接調用註解放方法;
  • 若事務可用,則創建一個實現了 TransactionSynchronization 接口的事務同步操作 TransactionSynchronizationEventAdapter ,並將其註冊到 TransactionSynchronizationManager
  • TransactionSynchronizationManager 在事務進行到指定階段後,會調用 TransactionSynchronizationEventAdapter 的對應回調方法;
  • TransactionSynchronizationEventAdapter 在回調方法中,再確認當前事務階段與在 @TransactionalEventListenerphase 屬性指定的階段是否一致,若一致則調用註解方法;

因此此處也不難理解爲什麼廣播器進行廣播的時候,若指定了線程池則事務會失效了,因爲具體到監聽器適配器調用時,通過 TransactionSynchronizationManager 註冊的事務是當前線程池中的工作線程的事務,而調用廣播器的主線程的事務與其不是一個事務,因此監聽器中事務回滾不會令主線程的事務一併回滾。

總結

當我們向 ApplicationContext 推送事件時:

  • ApplicationContext 會將事件推送至內部持有的廣播器實例 SimpleApplicationEventMulticaster

  • SimpleApplicationEventMulticaster 在內部維護了兩個檢索器,分別是 DefaultListenerRetrieverCachedListenerRetriever

    前者用於提供監聽器註冊和全量查詢功能,後者用於提供基於事件類型與事件源類型對監聽器進行快速查詢的緩存,

    當推送事件時,會先通過 DefaultListenerRetriever 全量查詢,然後將其加入 CachedListenerRetriever 緩存,下次查詢時直接從緩存中獲取訂閱了事件的監聽器;

  • 當獲得訂閱了事件的監聽器後,會嘗試調用監聽器的 onApplicationEvent 方法:

    1. 若廣播器中註冊了線程池,則會直接把操作提交到線程池中執行;
    2. 若廣播器中沒有註冊線程,則會直接在當前線程執行;
  • 監聽器被調用的時候,處理基本內的事件處理,而註解時監聽器還額外支持一些功能,比如:

    1. 如果使用了 @TransactionalEventListener 註解會生成支持事務的監聽器 ApplicationListenerMethodTransactionalAdapter ,該監聽器會在調用前向事務管理器註冊同步事務,從而獲得事務支持;
    2. 默認的註解式監聽器生成 ApplicationListenerMethodAdapter 這種類型的監聽器,該類監聽器在綁定的註解方法有返回值時,會嘗試將返回值也作爲一個事件發送,而如果是集合或者數組,則會攤平後嘗試將每一個元素都作爲事件發生;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章