深入理解Spring事件機制(一):廣播器與監聽器的初始化

前言

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

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

本文是其中的第一篇文章,將分析廣播器與監聽的是如何被初始化,並完成註解流程的。

在開始前,推薦先閱讀前文了解 Spring 容器的初始化過程與 BeanFactoryBean 的創建,如果可能,還可以瞭解一點 Spring 的註解機制,這將更有利於流程與一些代碼的理解。

相關文章:

一、廣播器的創建

在前文,我們知道容器的初始化是通過 AbstractApplicationContext.refresh() 方法完成的,事件機制的相關組件同樣也離不開容器,因此事件系統的初始化也通過該方法完成。

AbstractApplicationContext.initApplicationEventMulticaster() 是第一步,它的作用很簡單:

如果當前 BeanFactory 有名爲 “applicationEventMulticaster”ApplicationEventMulticaster,就把它設置爲當前上下文的事件廣播器,否則就創建並在 BeanFactory 中註冊一個SimpleApplicationEventMulticaster 實例作爲當前上下文的事件廣播器。

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 是否存在“applicationEventMulticaster”這個Bean
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        // 如果存在就把它設置爲當前上下文的事件廣播器
        this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 沒有就創建一個SimpleApplicationEventMulticaster作爲當前上下文的事件廣播器
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                         "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

二、編程式監聽器的註冊

4.2 及以前版本,監聽器需要顯式的實現 ApplicationListener 接口,我們管這種監聽器叫做編程式監聽器。

編程式監聽器在 AbstractApplicationContext.registerListeners() 這個方法的調用過程中被註冊到註冊廣播器中,這一塊代碼邏輯也很簡單:

  • 向事件廣播器註冊已經被註冊的 BeanFactroy 中,且實現了 ApplicationListener 接口的監聽器;
  • 向事件廣播器註冊還沒有被實例化的監聽器的 BeanName
  • 發佈一些早期事件;
protected void registerListeners() {
    // 向事件廣播器註冊已經被註冊的上下文中的監聽器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 向事件廣播器註冊指定的監聽器,不過這裏只註冊BeanName,
    // 因爲有些監聽器Bean是由FactoryBean生產的,而在這裏FactoryBean實際上還沒被生成出來
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 發佈一些早期事件
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

我們需要注意的是,在這一步,雖然向廣播器註冊了監聽器,但是實際上這只是一種關係,真正的監聽器實例不一定有被創建出來

不過在如果上下文中存在“早期事件”,則會觸發廣播,此時調用 ApplicationEventMulticaster.multicastEvent() 將會提前觸發廣播器中那些監聽器的初始化,否則按正常情況這些將等到上下文主動初始化 BeanFactory 中全部非懶加載 Bean 的時候纔會一併初始化。

三、註解式監聽器的註冊

4.2 版本以後,我們可以通過在成員方法上添加 @EventListener 或者 @TransactionalEventListener 註解的方法聲明一個監聽器,我們管這種監聽器叫做註解式監聽器。

實際上,由於註解式監聽器的類上沒有註解或接口作爲標識,因此無法直接從 BeanFactory 中查找,所以它的註冊顯然不能與編程式監聽器一樣,在 AbstractApplicationContext.registerListeners() 通過從 BeanFactory 中直接找到然後註冊。

3.0 以後支持的一些註解式配置的原理一樣,@EventListener 是通過 EventListenerMethodProcessor 這個特殊的後置處理器完成註冊的。

EventListenerMethodProcessor 實現的接口如下:

public class EventListenerMethodProcessor
    implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
}

其中, SmartInitializingSingletonBeanFactoryPostProcessor 接口非常直觀的告訴了我們它被調用的時機:

  • BeanFactoryPostProcessor:在上下文初始化的時候,通過 AbstractApplicationContext.invokeBeanFactoryPostProcessors 這個方法跟其他 BeanFactory 的後置處理器被一起集中調用;
  • SmartInitializingSingleton:在這個 Bean 完成初始化的時候;

接下來我們通過處理器分析註解式監聽器的註冊流程。

1、監聽器方法處理器的註冊

根據前文,我們知道容器在初始化過程中,通過 AbstarctApplicationContext.obtainFreshBeanFactory 創建新 BeanFactory 的時候,最終會一路繞到 AbstractRefreshableApplicationContext.loadBeanDefinitions 這個方法上,通過這個方法上下文會爲自己的 BeanFactory 提前加載好 BeanDefinition

而這個抽象方法在不同的上下文會有不同的實現,但是基本都要通過不同的 BeanDefinitionReader 去完成這個過程。

支持註解式配置的上下文會用 AnnotatedBeanDefinitionReader 去讀取配置的時候,會通過 AnnotationConfigBeanDefinitionParser 將配置信息解析爲具體的 BeanDefinition 。而 Spring 就在這一步將默認配置的一些 BeanBeanDefinition 給加上了。

實際上,不止 EventListenerMethodProcessor ,幾乎所有針對 Spring 註解的後置處理器都是通過這種方式註冊到 BeanFactory 的。

具體代碼參見 AnnotationConfigUtils.registerAnnotationConfigProcessors 方法:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {
    // 其他的一些註解處理器,比如 @Configuration,@Autowrite 之類的註解處理器... ...
    
    // 註冊 EventListenerMethodProcessor
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    // 註冊名爲“org.springframework.context.event.internalEventListenerFactory” EventListenerFactory
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}
    
    return beanDefs;
}

2、獲取監聽器工廠

EventListenerMethodProcessor 被作爲一個 BeanFactoryPostProcessor 被調用時,它會從 BeanFactory 中收集所有實現了 EventListenerFactory 接口的 Bean,然後記錄在成員變量 eventListenerFactories 中:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    this.beanFactory = beanFactory;
    Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(factories);
    this.eventListenerFactories = factories;
}

而監聽器工廠這個類作用也顯而易見,他用於把被註解的方法適配爲監聽器對象:

public interface EventListenerFactory {
    // 是否支持處理該方法
    boolean supportsMethod(Method method);
    // 將bean中帶有@EventListener註解的方法轉爲ApplicationListener
    ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);
}

值得一提的是,由於註冊 EventListenerMethodProcessor 的時候也會默認支持一個名爲 :"org.springframework.context.event.internalEventListenerFactory"DefaultEventListenerFactory,這保證至少有一個保底的監聽器工廠。

EventListenerFactory 提供兩個默認的實現:

  • DefaultEventListenerFactory:默認的實現,支持處理所有被 @EventListener 註解的方法,

    會將方法適配成類型爲 ApplicationListenerMethodAdapter 的監聽器;

  • TransactionalEventListenerFactory:支持 Spring 事務機制的監聽器的工廠, 用於處理被 @TransactionalEventListener 註解的方法,

    會將方法適配成類型爲 ApplicationListenerMethodTransactionalAdapter 的監聽器;

3、將方法適配爲監聽器

EventListenerMethodProcessor 作爲一個 SmartInitializingSingleton 被調用的時候:

public void afterSingletonsInstantiated() {
    ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    // 獲取工廠中的全部 Bean
    String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    for (String beanName : beanNames) {
        if (!ScopedProxyUtils.isScopedTarget(beanName)) {
            Class<?> type = null;
            try {
                // 如果是代理對象,則獲取原始對象的類型
                type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
            }
            catch (Throwable ex) {
                // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            if (type != null) {
                // 實現了ScopedObject接口
                if (ScopedObject.class.isAssignableFrom(type)) {
                    try {
                        // 獲取原始的Bean對象
                        Class<?> targetClass = AutoProxyUtils.determineTargetClass(
                            beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
                        if (targetClass != null) {
                            type = targetClass;
                        }
                    }
                    catch (Throwable ex) {
                        // An invalid scoped proxy arrangement - let's ignore it.
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
                        }
                    }
                }
                try {
                    // 處理 Bean
                    processBean(beanName, type);
                }
                catch (Throwable ex) {
                    throw new BeanInitializationException("Failed to process @EventListener " +
                                                          "annotation on bean with name '" + beanName + "'", ex);
                }
            }
        }
    }
}

拋開對代理對象的一些檢驗和處理,我們直接看看 processBean 方法:

private void processBean(final String beanName, final Class<?> targetType) {
    if (!this.nonAnnotatedClasses.contains(targetType) &&
        // targetType類名不以“java.”開頭,且不爲Ordered接口
        AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
        // 是未被@Component註解的Spring內部類
        !isSpringContainerClass(targetType)) {

        Map<Method, EventListener> annotatedMethods = null;
        try {
            // 查找直接或間接帶有@EventListener註解的方法
            annotatedMethods = MethodIntrospector.selectMethods(targetType,
                                                                (MethodIntrospector.MetadataLookup<EventListener>) method ->
                                                                AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
        }
        catch (Throwable ex) {
            // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
            if (logger.isDebugEnabled()) {
                logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
            }
        }

        // 如果該類沒有直接或間接帶有@EventListener註解的方法,則記錄並在下次查詢時跳過
        if (CollectionUtils.isEmpty(annotatedMethods)) {
            this.nonAnnotatedClasses.add(targetType);
            if (logger.isTraceEnabled()) {
                logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
            }
        }
        else {
            // Non-empty set of methods
            ConfigurableApplicationContext context = this.applicationContext;
            Assert.state(context != null, "No ApplicationContext set");
            List<EventListenerFactory> factories = this.eventListenerFactories;
            Assert.state(factories != null, "EventListenerFactory List not initialized");
            // 遍歷註解方法,並遍歷監聽器工廠
            for (Method method : annotatedMethods.keySet()) {
                for (EventListenerFactory factory : factories) {
                    // 若工廠支持處理
                    if (factory.supportsMethod(method)) {
                        // 將方法包裝爲ApplicationListener
                        Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                        ApplicationListener<?> applicationListener =
                            factory.createApplicationListener(beanName, targetType, methodToUse);
                        // 如果監聽器類型爲ApplicationListenerMethodAdapter,則需要傳入專門的SpEL表達式解析器EventExpressionEvaluator用於支持@EventListener.condition屬性
                        if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                            ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                        }
                        // 將監聽器加入中的
                        context.addApplicationListener(applicationListener);
                        break;
                    }
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                             beanName + "': " + annotatedMethods);
            }
        }
    }
}

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    this.applicationListeners.add(listener);
}

Spring 在這一步主要趁着 EventListenerMethodProcessorBeanFactory 中初始化的時候幹了兩件事:

  • 檢查 BeanFactory 中的所有的 Bean,篩選出其中有成員方法直接或間接帶有 @EventListener 註解的 Bean
  • 將此類 Bean 的方法通過 EventListenerFactory 封裝爲 ApplicationListener 對象;
  • 然後將這些轉換後得到的 ApplicationListener 註冊到上下文中的廣播器中;

此外,這裏有一個比較有意思的細節,就是由於 @EventListener 註解是支持在 condition 中通過 SpEL 表達式進行一些判斷的,因此在這一步,針對默認的監聽適配器實現 ApplicationListenerMethodAdapter ,提供了一個 init 方法用於把 SpEL 表達式解析器塞進去:

if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}

換而言之,如果我們希望讓 @EventListener.condition 支持更多功能,就可以在這個地方動點手腳,比如向 SpEL 表達式上下文註冊更多變量。

4、監聽器的註冊

上一節中提到,在 EventListenerMethodProcessor.processBean 將方法轉換爲 ApplicationListener 後會將其注入廣播器:

public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
        // 註冊到上下文中的廣播器中
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    this.applicationListeners.add(listener);
}

AbstractApplicationContext 會將該方法代理到內部持有的廣播器實例的 ApplicationEventMulticaster.addApplicationListener 方法:

public void addApplicationListener(ApplicationListener<?> listener) {
   synchronized (this.defaultRetriever) {
      // Explicitly remove target for a proxy, if registered already,
      // in order to avoid double invocations of the same listener.
      Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
      if (singletonTarget instanceof ApplicationListener) {
         this.defaultRetriever.applicationListeners.remove(singletonTarget);
      }
      this.defaultRetriever.applicationListeners.add(listener);
      this.retrieverCache.clear();
   }
}

該方法最終將監聽器添加到廣播器持有的 DefaultListenerRetriever 對象實例中,跟已經註冊到其中的編程式監聽器一起,以待後續使用。

四、監聽器工廠

通過上文,我們知道註解式監聽器依賴監聽器工廠 EventListenerFactoryBean 中的註解方法轉爲 ApplicationListener 實例。

實際上,我們知道 spring 除了支持 @EventListener 註解外,還提供了 @TransactionalEventListener 註解,用於註冊支持事務的註解式監聽器,因此 EventListenerFactory 實際上也提供了兩類工廠分別用於支持這兩種實現:

  • DefaultEventListenerFactory:默認的實現,支持處理所有被 @EventListener 註解的方法,

    會將方法適配成類型爲 ApplicationListenerMethodAdapter 的監聽器;

  • TransactionalEventListenerFactory:支持 Spring 事務機制的監聽器的工廠, 用於處理被 @TransactionalEventListener 註解的方法,

    會將方法適配成類型爲 ApplicationListenerMethodTransactionalAdapter 的監聽器;

1、通用監聽器工廠

通用監聽器工廠的代碼及其簡單,它的特點如下:

  • 支持處理任何方法:supportsMethod 方法固定返回 true
  • 總是最晚被執行:getOrder 默認返回 Ordered.LOWEST_PRECEDENCE
  • 總是將註解方法適配爲 ApplicationListenerMethodAdapter 類型的監聽器;
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {

    private int order = LOWEST_PRECEDENCE;

    public void setOrder(int order) {
        this.order = order;
    }
    
    @Override
    public int getOrder() {
        return this.order;
    }
    
    @Override
    public boolean supportsMethod(Method method) {
        return true;
    }
    
    @Override
    public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
        return new ApplicationListenerMethodAdapter(beanName, type, method);
    }

}

2、事務監聽器工廠

事件監聽器工廠代碼也並不複雜,相比 DefaultEventListenerFactory,它的特點如下:

  • 默認比 DefaultEventListenerFactory 更先被調用:getOrder 默認返回 50,比 DefaultEventListenerFactory 返回的 Ordered.LOWEST_PRECEDENCE 值更小;
  • 僅支持處理直接或間接被 @TransactionalEventListener 註解的方法;
  • 總是將註解方法適配爲 ApplicationListenerMethodTransactionalAdapter 類型的監聽器;
public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered {

    private int order = 50;

    public void setOrder(int order) {
        this.order = order;
    }

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


    @Override
    public boolean supportsMethod(Method method) {
        return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);
    }

    @Override
    public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
        return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
    }

}

總結

當 Spring 容器啓動,上下文調用 AbstractApplicationContext.refresh 方法對其進行初始化時,Spring 事件機制的兩個核心組件:廣播器、監聽器也在該過程完成初始化。

  • AbstractApplicationContext.initApplicationEventMulticaster 這一步,Spring 爲上下文創建並掛載了廣播器 ApplicationEventMulticaster 的實例;

  • AbstractApplicationContext.registerListeners 這一步,Spring 將容器中所有實現了 ApplicationListener 接口的 Bean 註冊到廣播器中;

  • 而在 AbstractApplicationContext.finishBeanFactoryInitialization 這一步,Spring 會初始化容器中所有非懶加載的 Bean,此時會一併實例化 EventListenerMethodProcessor 這個 Bean 後置處理器:

    1. 這個 Bean 由上下文在準備 BeanFactory 時,調用 loadBeanDefinitions 方法時註冊到容器;

    2. 由於 EventListenerMethodProcessor 本身實現了 SmartInitializingSingleton 接口,因此實例化後會觸發其回調函數,此時 EventListenerMethodProcessor 會把容器中所有的 Bean 都拿出來解析;

    3. Bean 中存在直接或間接被 @EventListener 註解的方法,則會將其取出,並通過 EventListenerFactory 將其適配爲對應的 ApplicationListener 實例,然後再將適配完的監聽器註冊到容器中;

    4. 由於 Spring 除了 @EventListener 註解還提供了支持事務的@TransactionalEventListener 註解,因此提供了兩類監聽器工廠:

      DefaultEventListenerFactory:默認的實現,支持處理所有被 @EventListener 註解的方法;

      TransactionalEventListenerFactory:支持 Spring 事務機制的監聽器的工廠, 用於處理被 @TransactionalEventListener 註解的方法;

至此,當 AbstractApplicationContext.refresh 執行完畢,即上下文初始化完成後,廣播器與所有編程式或註解式監聽器皆初始化完畢,並且完成了註冊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章