ApplicationEventPublisher详解

ApplicationEventPublisher是ApplicationContext的父接口之一。 这接口的作用是:Interface that encapsulates event publication functionality.(用于封装事件发布功能的接口)

在这里插入图片描述

在这里我们就看到了,Spring底层对ApplicationEvent事件和PayloadApplicationEvent事件的处理:如果发布的直接就是ApplicationEvent类型的事件,那么就直接转换成ApplicationEvent类型,而如果不是ApplicationEvent类型的事件,那么就是我们所说的Object类型的事件,就会帮我们封装成PayloadApplicationEvent(ApplicationEvent子类),并将Object类型的事件信息存储到payload属性中。在处理完事件的类型后,执行了下面这行重要的代码:

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

其中getApplicationEventMulticaster方法是拿到容器中的事件广播器,然后通过这个事件广播器来进行事件的广播。那么这个事件多播器我们也没有配置创建,是怎么获取的呢?

这就需要看一下Spring容器刷新的9大步当中的initApplicationEventMulticaster初始化事件广播器的方法了,如下:
在这里插入图片描述

一看代码,还是Spring的老技巧,有了就用你的,没有我就帮你做一个然后用我的:首先,Spring会查看容器中有没有名称为applicationEventMulticaster的bean对象,如果有的话,就返回这个对象;如果没有的话,Spring就会帮我们new一个SimpleApplicationEventMulticaster的事件广播器对象返回。

所以,我们默认就是走的SimpleApplicationEventMulticaster中的multicastEvent事件广播逻辑,代码如下:
在这里插入图片描述

Spring正式通过拿到容器中所有符合当前事件的监听器,然后循环遍历挨个调用onApplicationEvent方法。

@EventListener的实现

除了实现ApplicationListener接口的方式来实现监听器,在走向注解驱动开发后,Spring同样也为我们提供了@EventListener这个注解来实现监听器,注解使用起来还是十分的方便的:

只需要在Spring托管Bean的public方法上加上@EventListener注解就可以监听事件了,想监听的事件类型也可以通过注解的属性来进行配置,例如像这样:就只会监听容器刷新的ContextRefreshedEvent事件。
在这里插入图片描述

@EventListener是为了支持注解开发,在Spring Framework 4.2的时候才新增的。我们先不看Spring底层是如何实现的,如果说现在让我们来开发一个注解来实现事件监听的功能,

那你会怎么做呢,如果开始我们和Spring家想的一样:只需要在Spring托管Bean的public方法上加上@EventListener注解就可以监听事件了,而我们又知道Spring的底层是循环遍历

  1. 支持该事件的监听器集合来进行广播事件的,那么现在为了让我们这个注解也能达到监听事件的效果,即也能被广播到,那么我觉得我们可以想到两种方式来实现:
    新建一个集合,将所有标有@EventListener方法的bean都放到这个集合里面,当进行事件广播的时候,同时也循环遍历这个集合,来触发可以监听这个事件的监听器。
  2. 使用适配器模式,将标有@EventListener注解的方法适配成ApplicationListener(其实,全类名,以及方法名,方法参数都有了,就可以通过反射来执行到适配之前的方法了),然后一起放到Spring储存事件监听器的集合中去。

那么,Spring的底层是采用什么的方式来实现@EventListener能够进行监听的呢?其实,Spring家就是使用第二种适配器模式来完成的,既然我们知道了Spring是使用这种方式实现的,那么我们继续再想想需要在Spring Framework 3.0版本的基础上增加哪些组件呢:

  1. 我们需要找到所有标有@EventListener方法的bean,其实,就这一问题的解决方案有很多,主要看是在每个bean实例化的过程中就行判断还是说等所有的bean都实例化完了,在最后一起就行判断,而Spring的底层是选择了后者,通过新增一个实现了SmartInitializingSingleton接口的EventListenerMethodProcessor,我们知道在容器刷新快结束的时候,也就是所有的bean都实例化完成之后,Spring会挨个调用SmartInitializingSingleton的afterSingletonsInstantiated方法,Spring也正是在此进行处理标有@EventListener的bean的
  2. 另外,就是得提供一个适配器,来完成@EventListener注解到ApplicationListener的适配,所以,在SpringFramework 4.2的版本中,引入了ApplicationListenerMethodAdapter这个适配类
  3. 为了方便构造出ApplicationListenerMethodAdapter这个适配器对象,Spring还新增了一个EventListener工厂类DefaultEventListenerFactory来帮助我们构建ApplicationListenerMethodAdapter对象,或许这就是优秀代码人的代码能力体现吧,是我的话,可能就直接自己new了

上面已经说明了新增了哪些组件了,以及需要新增的原因,下面我们看看EventListenerMethodProcessor以及DefaultEventListenerFactory这两个组件是什么时候注入到容器中的,在容器构造函数中,创建AnnotatedBeanDefinitionReader对象的时候调用registerAnnotationConfigProcessors方法进行了注入,代码如下:
在这里插入图片描述

看着两个if写在最后,我们就猜得到是后期为了支持注解@EventListener而后加上去的,只要我们自己没有注入名字为org.springframework.context.event.internalEventListenerProcessor以及名字为org.springframework.context.event.internalEventListenerFactory的bean定义信息的时候,Spring就会帮我们注入EventListenerMethodProcessor以及DefaultEventListenerFactory这两个类型的bean定义信息,

从而在后期帮忙实现@EventListener监听的功能,下面就来大致看看具体是如何实现的吧:
首先来看看EventListenerMethodProcessor的afterSingletonsInstantiated方法:

这个方法,是完成spring容器中beanDefinitionMap转换为spring容器中的bean这步的关键方法,这个方法中,可以分为两部分

1.将beanDefinitionMap中的beanDefinition转换为bean,放入到spring容器中

2.在初始化完成之后,调用smartSingleton.afterSingletonsInstantiated();

所以,我们可以认为,对于@EventListener注解的解析,就是在这里被调用的:

org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated

@Override
public void afterSingletonsInstantiated() {
  /**
   * 1.获取到所有的eventListenerFactory,理论上就是DefaultEventListenerFactory
   */
  List<EventListenerFactory> factories = getEventListenerFactories();
  ConfigurableApplicationContext context = getApplicationContext();
  /**
   * 2.获取到spring容器中,所有的beanName,依次遍历
   */
  String[] beanNames = context.getBeanNamesForType(Object.class);
  for (String beanName : beanNames) {
    if (!ScopedProxyUtils.isScopedTarget(beanName)) {
      Class<?> type = null;
      try {
        type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), 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(
                context.getBeanFactory(), 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);
            }
          }
        }
        /**
         * 从for循环开始,到这里,代码的细节没有看的太懂,但是这段代码是为了获取到beanName对应的type
         * ,以便在下面使用
         */
        try {
          processBean(factories, beanName, type);
        }
        catch (Throwable ex) {
          throw new BeanInitializationException("Failed to process @EventListener " +
              "annotation on bean with name '" + beanName + "'", ex);
        }
      }
    }
  }
}

processBean的代码逻辑

/**
 * 在这个方法中,会解析beanName对应的bean中所有的方法上,是否有添加@EventListener注解,如果有添加
 *  则根据beanName生成一个applicationListener,并添加到多事件派发器上
 * @param factories
 * @param beanName
 * @param targetType
 */
protected void processBean(
    final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {

  /**
   * 1.nonAnnotatedClasses
   * 这个set集合,可以理解为是一个缓存,全局搜索了下,只有在下面,annotatedMethods为null的时候,才会写入到这个集合中
   * 所以,我认为,在第一次解析的时候,如果一个bean中的method没有条件@EventListener注解,就会把这个bean
   * 添加到nonAnnotatedClasses集合中
   */
  if (!this.nonAnnotatedClasses.contains(targetType)) {
    Map<Method, EventListener> annotatedMethods = null;
    try {
      /**
       * 2.解析targetType这个class,获取类中所有加了@EventListener注解的method
       */
      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);
      }
    }
    /**
     * 3.如果当前class的方法中,没有添加@EventListener注解,就添加到nonAnnotatedClasses中
     */
    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
      /**
       * 4.如果当前targetClass中,有方法添加了@EventListener注解,那就根据beanName
       * 生成一个applicationListener
       * 方法,点进去看,会发现,就是ApplicationListenerMethodAdapter这个类
       *
       * 所以,我们可以认为:
       * 1、对于实现ApplicationListener接口这种方式声明的事件监听器,其applicationListener就是对应的实现类
       * 2、对于使用@EventListener注解这种方法,其applicationListener就是ApplicationListenerMethodAdapter
       */
      ConfigurableApplicationContext context = getApplicationContext();
      for (Method method : annotatedMethods.keySet()) {
        for (EventListenerFactory factory : factories) {
          if (factory.supportsMethod(method)) {
            Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
            // 这里生成的applicationListener
            // 是ApplicationListenerMethodAdapter
            ApplicationListener<?> applicationListener =
                factory.createApplicationListener(beanName, targetType, methodToUse);
            if (applicationListener instanceof ApplicationListenerMethodAdapter) {
              ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
            }
            /**
             * 5.最重要得一步:将applicationListener
             * 添加到多事件派发器中,在后面publishEvent的时候会用到
             */
            context.addApplicationListener(applicationListener);
            break;
          }
        }
      }
      if (logger.isDebugEnabled()) {
        logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
            beanName + "': " + annotatedMethods);
      }
    }
  }
}

保存事件监听器
context.addApplicationListener(applicationListener);
这一行代码,就是来保存事件监听器的

org.springframework.context.support.AbstractApplicationContext#addApplicationListener
@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);
}

在这个方法中,我们只需要关注一行代码

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
  synchronized (this.retrievalMutex) {
    // 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();
  }
}

所以,我们可以看到,对于这里的逻辑,其实就是存到了内存中的一个set集合

调用事件监听器
在前面解析完了事件监听器之后,并且也存入到了set集合中,最后会在spring容器刷新完成之后,统一调用事件监听器的方法

org.springframework.context.support.AbstractApplicationContext#finishRefresh
    org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
        org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  /**
   * 获取所有的事件监听器,判断是否是异步执行
   * 但是下面这一步getApplicationListeners()是比较重要的一个方法,获取所有的listener
   * 我们可以暂时认为是从前面的set集合中获取到对应的事件监听器
   */
  for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    Executor executor = getTaskExecutor();
    if (executor != null) {
      executor.execute(() -> invokeListener(listener, event));
    }
    else {
      invokeListener(listener, event);
    }
  }
}

根据上面的代码,我们了解了Spring底层的处理逻辑是:首先拿到容器中的所有bena对象的名称,然后遍历没一个对象进行判断它是否有标有@EventListener注解的方法,有的话就存储起来,最后来遍历具有@EventListener注解的方法的bean,通过适配器工厂EventListenerFactory将其适配为ApplicationListener对象,最后放入到监听器集合中。你看是不是和我们前面想的方案二是一样的所以说,当我们掌握了Spring的扩展点后,自己来完成一些新的功能也是没有问题的。

事件使用:https://blog.csdn.net/qq_37171353/article/details/115767381
发布流程:https://blog.csdn.net/ITlikeyou/article/details/124773814
监听加载流程:https://blog.csdn.net/CPLASF_/article/details/119118520

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring ApplicationEventPublisherSpring 框架中的一个接口,用于发布应用程序的事件。通过 ApplicationEventPublisher,我们可以在应用程序中定义和发布自定义事件,并将其传播到对该事件感兴趣的监听器。 Spring 框架基于观察者设计模式,通过 ApplicationEventPublisherApplicationListener 来实现事件发布和监听。当我们需要在应用程序中触发一些事件时,可以创建一个实现了 ApplicationEvent 类的自定义事件,并使用 ApplicationEventPublisher 将该事件发布出去。 使用 ApplicationEventPublisher 的方式有两种: 1. 通过实现 ApplicationEventPublisherAware 接口自动注入 ApplicationEventPublisher 实例。这样就可以在任何需要发布事件的地方直接调用 ApplicationEventPublisherpublishEvent 方法。 2. 在 Spring 容器中注入 ApplicationEventPublisher 实例,然后在需要发布事件的地方手动调用 publishEvent 方法。 发布事件之后,事件会被传播给所有实现了 ApplicationListener 接口的监听器。通过实现 ApplicationListener 接口,我们可以自定义事件的监听器,并在监听器中编写逻辑,处理事件触发后的业务逻辑。 Spring 框架的事件机制可以使我们的应用程序更加模块化和可扩展。我们可以定义多个事件和监听器,实现不同模块之间的解耦。同时,通过事件机制,我们也可以更方便地实现日志记录、业务流程控制等功能。 总之,Spring ApplicationEventPublisherSpring 框架中的一个重要组件,用于实现事件发布和监听。通过定义自定义事件和监听器,我们可以实现应用程序的模块化和解耦,提高代码的可维护性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值