Spring事件监听原理

2023年 10月 11日 111.6k 0

Spring 事件监听原理

基于 SpringBoot-3.1.2

Spring 事件机制主要用于业务编码的解耦,例如用户订单办理成功,需要发送短信通知,这是两个不同的业务逻辑,不应该耦合在一起,针对于此,就可以通过事件机制来解决,以下是一个最简单的Spring事件使用示例

定义事件对象

import lombok.Data;

@Data
public class EventObj {
    private String name;
    public EventObj(String name) {
        this.name = name;
    }
}

准备事件监听器(也就是发布事件后,对应的处理者)

@EventListener
public void listener1(EventObj event) {
    log.info(event.getName());
}

注入事件发布器(由 Spring 提供)并使用事件发布器进行事件的发布

// 注入事件发布器
private final ApplicationEventPublisher publisher;

// 发布事件
publisher.publishEvent(new EventObj("普通事件"));

单纯根据 Spring 事件 API 的调用形式来看,采用了经典的观察者模式来实现,publisher 对象作为发布者,被@EventListener注解标识的方法作为具体的观察者。

那么 Spring 的事件是怎样发布的,又是如何只通过一个简单的@EventListener注解就将对应的方法注册为了观察者,事件监听方法又是如何与对应的事件对象匹配,这需要探究其中源码的具体实现

事件发布调用流程

Spring 的源码繁多,想要准确的找到某个功能在源码层面中的具体位置也许并不简单,但我们可以根据功能的使用情况,在应用层面打上断点,比如这里的事件发布,我们可以在listener1方法接收事件对象的地方打上断点,再通过调用堆栈查看源码到底是怎样的调用流程,如下

@EventListener
public void listener1(EventObj event) {
	// 将此行代码断点
    log.info(event.getName());
}

启动项目,发布事件,我们会得到如下调用堆栈

其中sendEvent是我们业务方法发布事件的地方,再往下,进入了 Spring 的事件发布方法

/**
 * Publish the given event to all listeners.
 * 

Note: Listeners get initialized after the MessageSource, to be able * to access it within listener implementations. Thus, MessageSource * implementations cannot publish events. * @param event the event to publish (may be an {@link ApplicationEvent} * or a payload object to be turned into a {@link PayloadApplicationEvent}) */ @Override public void publishEvent(Object event) { publishEvent(event, null); }

继续进入下一步的publishEvent(event, null);逻辑,有如下关键源码

......
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
    this.earlyApplicationEvents.add(applicationEvent);
}
else {
    // 实际调用
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
......

到这里,我们可以猜测,通过getApplicationEventMulticaster()获取了一个事件发布器,然后调用multicastEvent()将事件发布出去,即找到对应的事件监听方法,将事件源交给其处理。

事实也是如此,multicastEvent(applicationEvent, eventType)方法的源码如下

@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
    Executor executor = getTaskExecutor();
    // 获取这个事件对应的监听器集合,也就是被 @EventListener 注解标识的事件监听器
    // 这也说明,同一个事件对象可以被多个 @EventListener 注解标识的事件监听方法所处理
    for (ApplicationListener listener : getApplicationListeners(event, type)) {
        // 是否使用了线程池(异步方法)
        if (executor != null) {
            // 使用线程池来异步执行事件处理逻辑
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            // 否则同步调用事件处理逻辑
            invokeListener(listener, event);
        }
    }
}

继续跟踪源码到doInvoke()方法中,部分源码如下

protected Object doInvoke(Object... args) {
    Object bean = getTargetBean();
    // Detect package-protected NullBean instance through equals(null) check
    if (bean.equals(null)) {
        return null;
    }

    ReflectionUtils.makeAccessible(this.method);
    try {
        // 调用目标方法,即对应的事件处理方法
        return this.method.invoke(bean, args);
    }
    ......

事件监听关系绑定

通过上面的分析,整个事件从发布到调用的流程已经大致结束了,但仍然存在一些疑问,在目前的源码中,并没有看到 Spring 是怎样将事件对象和具体的事件监听方法进行绑定的。

这说明这种关联关系并不是在调用时的时候建立的,而根据 Spring 的一贯套路,这种关系极有可能在容器启动刷新的时候,通过一些后处理器增强来实现,想要观察到这里面的源码,我们需要从上面的doInvoke()方法中查找蛛丝马迹,可以看到,目标方法是通过this.method得到和调用的,这里就可以确定,Sping 将事件对象和对应事件监听方法的关系绑定到了doInvoke()方法所属的类org.springframework.context.event.ApplicationListenerMethodAdapter

那么只需要将断点打在ApplicationListenerMethodAdapter的构造方法,就可以探究这种绑定关系是怎样建立的,由此我们可以得到如下调用栈

可以看到,从 main 方法,一直到容器刷新的finishBeanFactoryInitialization(beanFactory)调用处,这是 Spring 实例和初始化所有非懒加载单例 bean 的地方,继续跟进,在finishBeanFactoryInitialization(beanFactory)方法的最后,真正进行 bean 的实例和初始化

// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

preInstantiateSingletons()方法的源码如下

@Override
public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List beanNames = new ArrayList(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof SmartFactoryBean smartFactoryBean && smartFactoryBean.isEagerInit()) {
                    getBean(beanName);
                }
            }
            else {
                getBean(beanName);
            }
        }
    }

    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        // 实现了 SmartInitializingSingleton 接口的 Bean, 会执行初始化完成之后的生命周期
        // 此时容器中的所有 Bean 已经完成了实例和初始化, 这里的初始化主要用于处理多个 Bean 之间的依赖关系
        if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
            StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
                    .tag("beanName", beanName);
            // 执行 Bean 的 afterSingletonsInstantiated() 初始化生命周期
            smartSingleton.afterSingletonsInstantiated();
            smartInitialize.end();
        }
    }
}

通过 debug 的调用堆栈可以看到,ApplicationListenerMethodAdapter的创建,正是由 beanNameorg.springframework.context.event.internalEventListenerProcessorbean 中的afterSingletonsInstantiated()调用触发,如下图

而通过internalEventListenerProcessor这个beanName实际获取到的 bean 为org.springframework.context.event.EventListenerMethodProcessor,其定义如下

public class EventListenerMethodProcessor
    implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
    ......

可以看到,它实现了SmartInitializingSingletonBeanFactoryPostProcessor接口,说明其中会包含一些增强逻辑,但事件监听关系并不在这里绑定,这里之所以实现BeanFactoryPostProcessor接口,是为了在 Spring 容器刷新时,通过postProcessBeanFactory()方法执行一些初始化(后面会说明)

真正处理 Spring 事件相关的逻辑,在afterSingletonsInstantiated方法中定义。

继续跟踪堆栈,afterSingletonsInstantiated()方法源码如下

@Override
public void afterSingletonsInstantiated() {
    ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    // 遍历每个 bean, 然后调用 processBean(beanName, type) 对其执行 Spring 事件相关的增强逻辑
    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) {
                if (ScopedObject.class.isAssignableFrom(type)) {
                    try {
                        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 {
                    processBean(beanName, type);
                }
                catch (Throwable ex) {
                    throw new BeanInitializationException("Failed to process @EventListener " +
                            "annotation on bean with name '" + beanName + "': " + ex.getMessage(), ex);
                }
            }
        }
    }
}

主要流程为

  • 遍历每个 bean
  • 对每个 bean 执行processBean(beanName, type)方法

processBean(beanName, type)方法源码如下

private void processBean(final String beanName, final Class targetType) {
    // nonAnnotatedClasses 用于存储未标注 @EventListener 注解的类, 减少不必要的扫描
    /*
    调用 AnnotationUtils.isCandidateClass(targetType, EventListener.class) 检查
    当前 bean 中是否携带了 @EventListener 注解
    */
    if (!this.nonAnnotatedClasses.contains(targetType) &&
            AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
            !isSpringContainerClass(targetType)) {

        Map annotatedMethods = null;
        try {
            // 查找到当前 bean 中, 标识了 @EventListener 注解的方法
            annotatedMethods = MethodIntrospector.selectMethods(targetType,
                    (MethodIntrospector.MetadataLookup) 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 注解的方法, 说明这不是一个监听器类, 将其加入 @EventListener 集合
        // 避免重复扫描
        if (CollectionUtils.isEmpty(annotatedMethods)) {
            this.EventListener.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 factories = this.eventListenerFactories;
            Assert.state(factories != null, "EventListenerFactory List not initialized");
            // 遍历每个事件监听方法
            for (Method method : annotatedMethods.keySet()) {
                // 遍历事件监听器的创建工厂, 看那个工厂支持创建当前事件监听方法对应的事件监听器
                for (EventListenerFactory factory : factories) {
                    if (factory.supportsMethod(method)) {
                        // 获取事件监听方法
                        Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                        // 创建事件监听器
                        ApplicationListener applicationListener =
                                factory.createApplicationListener(beanName, targetType, methodToUse);
                        if (applicationListener instanceof ApplicationListenerMethodAdapter alma) {
                            alma.init(context, this.evaluator);
                        }
                        // 加入容器
                        context.addApplicationListener(applicationListener);
                        break;
                    }
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                        beanName + "': " + annotatedMethods);
            }
        }
    }
}

主要流程为

  • 检查当前 bean 中是否标识了@EventListener注解,如果有,则进入后续流程

  • 查找到当前 bean 中所有标识了@EventListener注解的方法,如果没有,则将当前 bean 放入nonAnnotatedClasses集合,避免重复扫描

  • 进入事件监听器的创建流程

    • 首先获取事件监听器的创建工厂集合eventListenerFactories,这个工厂集合是在 Spring 容器刷新时,通过调用invokeBeanFactoryPostProcessors(),为每个BeanFactoryPostProcessor调用其postProcessBeanFactory()方法时被赋值的,集合中默认存在org.springframework.context.event.DefaultEventListenerFactoryorg.springframework.transaction.event.TransactionalEventListenerFactory两种事件监听器的创建工厂,相关源码如下

      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
          this.beanFactory = beanFactory;
          // 获取 EventListenerFactory 类型的 bean
          Map beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
          List factories = new ArrayList(beans.values());
          AnnotationAwareOrderComparator.sort(factories);
          // 赋值给 EventListenerMethodProcessor 的 eventListenerFactories 属性
          // 后续在 processBean() 流程中会获取到
          this.eventListenerFactories = factories;
      }
      
    • 遍历事件监听方法,通过调用factory.supportsMethod(method)从事件监听器工厂中查找到支持通过当前事件监听方法创建事件监听器的工厂,这里有一个细节,TransactionalEventListenerFactory工厂的优先级要高于DefaultEventListenerFactory工厂,因为TransactionalEventListenerFactory是用于支持@TransactionalEventListener注解的,相对于@EventListener注解,@TransactionalEventListener注解的适用范围要小一些,因此先确认TransactionalEventListenerFactory工厂是否支持当前事件方法

    • 获取当前 bean 中的所有事件监听方法

    • 根据事件监听方法,调用事件监听器创建工厂的createApplicationListener(beanName, targetType, methodToUse)方法,创建对应的事件监听器,这里会调用到org.springframework.context.event.ApplicationListenerMethodAdapter的构造器,相关源码如下

      public ApplicationListenerMethodAdapter(String beanName, Class targetClass, Method method) {
          this.beanName = beanName;
          this.method = BridgeMethodResolver.findBridgedMethod(method);
          // 获取 Spring 事件处理的目标方法, 即被 @EventListener 或 @TransactionalEventListener 注解标识的方法
          this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
                  AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
          this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
      
          EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
         
          // 解析出当前事件方法实际支持的事件对象类型
          this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
          this.condition = (ann != null ? ann.condition() : null);
          this.order = resolveOrder(this.targetMethod);
          String id = (ann != null ? ann.id() : "");
          this.listenerId = (!id.isEmpty() ? id : null);
      }
      

      在上面的构造函数中,分别解析了事件处理方法以及事件处理方法支持的类型,将二者的关系保存到了ApplicationListenerMethodAdapter中,至此,【事件监听关系】绑定成功

根据事件绑定关系调用

在前面的内容中,我们将 Spring 事件从声明,到监听器的创建,监听关系的绑定进行了较为完整的分析,下面继续回到 Spring 事件发布调用的流程,详细了解 Sring 是怎么通过事件监听绑定关系调用到事件监听方法的

我们将断点继续打在事件监听方法体中,如下

@EventListener
public void listener1(EventObj event) {
	// 将此行代码断点
    log.info(event.getName());
}

发布事件,得到如下调用堆栈

有了前面的基础,我们可以快速来到multicastEvent()方法中,源码如下

@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
    Executor executor = getTaskExecutor();
    // 获取这个事件对应的监听器集合,也就是被 @EventListener 注解标识的事件监听器
    // 这也说明,同一个事件对象可以被多个 @EventListener 注解标识的事件监听方法所处理
    for (ApplicationListener listener : getApplicationListeners(event, type)) {
        // 是否使用了线程池(异步方法)
        if (executor != null) {
            // 使用线程池来异步执行事件处理逻辑
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            // 否则同步调用事件处理逻辑
            invokeListener(listener, event);
        }
    }
}

关键方法为getApplicationListeners(event, type),通过方法签名可以看出,这是根据事件类型来确定对应的事件监听器

getApplicationListeners(event, type)内部最终会调用到retrieveApplicationListeners(),其部分源码如下

// 此方法用于实际检索给定事件和源类型对应的事件监听器
private Collection> allListeners = new ArrayList();
    Set> listeners;
    Set listenerBeans;
    synchronized (this.defaultRetriever) {
        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
    }

 
    // 添加以编程方式注册的侦听器, 即通过 @EventListener 或 @TransactionalEventListener 注册的事件监听器
    for (ApplicationListener listener : listeners) {
        // 通过 supportsEvent(listener, eventType, sourceType) 方法来确定支持当前事件的监听器
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }

supportsEvent(listener, eventType, sourceType)方法源码如下

@Override
public boolean supportsEventType(ResolvableType eventType) {
    // declaredEventTypes 保存了当前事件监听器支持的事件类型, 这在上面的【事件监听关系绑定】流程中被确定
    for (ResolvableType declaredEventType : this.declaredEventTypes) {
        if (eventType.hasUnresolvableGenerics() ?
                declaredEventType.toClass().isAssignableFrom(eventType.toClass()) :
                declaredEventType.isAssignableFrom(eventType)) {
            return true;
        }
        if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
            if (eventType.hasUnresolvableGenerics()) {
                return true;
            }
            ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
            if (declaredEventType.isAssignableFrom(payloadType)) {
                return true;
            }
        }
    }
    return false;
}

上述逻辑,主要是在遍历判断当前监听器支持的事件类型列表中, 是否存在一个与事件类型相同的类型,如果存在,说明当前监听器支持处理当前事件。

最终会将所有支持当前发布时间的监听器都通过上面这种方式查找到并且返回,后续将通过事件监听器中的【事件监听绑定关系】调用到目标方法。

整个事件发布到监听调用的流程也就结束了。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论