阅读 145

Spring——事件处理机制

序章

今天有个哥突然问我,他在面试的时候被问道Nacos是怎么与Spring进行集成的?面试官又问了他观察者模式在Spring中的典型应用是什么??????

他当时都扯不下去了,最终面试失败;我当时真的很无语,当场咆哮道:“猪大肠!这两个问题其实是一个问题,面试官已经给足您老人家面子了,Nacos是使用Spring中的事件机制进行集成的,Spring的事件处理机制就是观察者模式的典型体现啊”;他一脸茫然的看作我.............;

一、开始

先不瞎逼逼,按常规步骤,先来一个示例程序;

1.1)创建一个自定义的业务Bean

public class ListenerTestBean1 {

    /**
     * 属性描述:标识
     *
     * @date : 2021/4/5 0005 下午 8:22
     */
    private Integer key;

    /**
     * 属性描述:内容
     *
     * @date : 2021/4/5 0005 下午 8:22
     */
    private String value;

    /**
     * 功能描述:监听器测试类——1
     *
     * @author : XXSD
     * @date : 2021/4/5 0005 下午 8:24
     */
    public ListenerTestBean1() {
    }

    .......此处省去get以及set方法.......
}
复制代码

1.2)创建测试事件对象

public class TextEvent extends ApplicationEvent {

    private static final long serialVersionUID = -572546991703136392L;
    /**
     * 属性描述:事件标识
     * @date : 2021/4/5 0005 下午 8:27
     */
    private final String eventKey;

    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public TextEvent(Object source, String eventKey) {
        super(source);
        this.eventKey = eventKey;
    }

    /**
    * 功能描述:事件标识
    * @author : XXSD
    * @date : 2021/4/5 0005 下午 8:28
    */
    public String getEventKey() {
        return eventKey;
    }
}
复制代码

1.3)创建监听器对象

@Component
public class ListenerTextEvent implements ApplicationListener<TextEvent> {
    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    @Override
    public void onApplicationEvent(TextEvent event) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.err.println("监听到事件:" + event.getSource());
        System.err.println("获取到的事件标识:" + event.getEventKey());
    }
}
复制代码

1.4)创建配置类及启动对象

@ComponentScan(basePackages = {"xuexi.listener"})
public class ListenerConfig {

}
复制代码
public static void main(String[] values){
    final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ListenerConfig.class);

    final ListenerTestBean1 listenerTestBean1 = new ListenerTestBean1(1);
    System.err.println("监听器测试类——1初始化完毕,开始推送事件");
    annotationConfigApplicationContext.publishEvent(new TextEvent(listenerTestBean1, "Test——1"));
    System.err.println("执行完毕");
}
复制代码

好了,到这里基本上一个简易的Demo就做好了,启动一下吧,看看效果;

二、事件基础

2.1)前置条件

作为一个完整的事件,必须具备3个必要的条件:

  • 事件对象:ApplicationEvent,用于描述事件的信息;
  • 事件发布器:ApplicationEventMulticaster,用于发布、推送事件,是设计模式中——观察者模式的体现,维护时间与事件监听器之间的对应关系,在事件发生时负责通知相关监听器进行处理;
  • 事件监听器:ApplicationListener,用于接收并对事件的信息做出响应处理;

这个概念如果有点抽象的话,这里举个例子:你的女朋友刚刚出门,你听见乌云密布,而且还打了一个雷,这时你拿起雨伞冲下楼去,把伞交到你女朋友手上;整个过程其实就是一个事件的发布、推送、处理的一个过程;乌云密布而且还打了一个雷触——发了一个事件;你拿起雨伞下楼交给你女朋友——对事件的响应及处理(不知您是否理解了这个例子,实在不行就换成:敌人将在3秒后抵达战场.........呵呵呵呵呵);

2.2)Spring中提供的事件

示例中,我们使用的是自己自定义的事件;其实在Spring中内置了一些事件;

clipboard.png 这些事件都是Spring内置的重要事件;

  • ContextRefreshedEvent

上下文处理事件,在容器被实例化或refreshed时触发,即:整个容器处理完毕之后(回忆一下之前说过的Spring核心的那几大步,在最后一步——finishRefresh()的时候会触发这个事件,触发事件的对象为:ContextRefreshedEvent);例如:在核心处理方法refresh方法被调用时;注意这里的“容器被实例”是指所有的Bean都被加载、后置处理器都被激活、所有单例Bean都被实例化完毕、容器中所有对象都已经准备就绪(可使用状态);

PS:注意一下,如果容器支持热重载,那么refresh可以被触发多次,注意:XmlWebApplicationContext支持热刷新;GenericApplicationContext不支持热刷新;这里有个看面试官心态(比如:本人,一般本人是比较变态的,哇哈哈哈哈哈哈)的面试题:“请问如何在所有Bean都创建完毕之后做一些事情?”其实就是这里的事件处理;这里啰嗦一下,可以建立一个自定义的监听器,用于监听这个事件:这个时候如果是我,可能还要问:除了监听这个事件以外还有什么办法呢?是不是很邪恶???大家都知道后置处理器是Spring的一个拓展点,那么这里顺便再告诉大家另外一个拓展点:SmartInitializingSingleton这个接口也是一个拓展点,在后面的章节会提到;

/**
 * 类描述:  ContextRefreshedEvent事件监听器
 * <br />
 * 这个事件是在Spring核心方法的最后一步执行触发;
 * 源码位置:org.springframework.context.support.AbstractApplicationContext#finishRefresh()
 * 源码:publishEvent(new ContextRefreshedEvent(this));
 *
 * @author XXSD
 * @version 1.0.0
 * @date 2021/4/6 0006 上午 10:18
 */
@Component
public class ContextRefreshedEventListener {

    /**
     * 功能描述:相应ContextRefreshedEvent的事件处理
     *
     * @author : XXSD
     * @date : 2021/4/6 0006 上午 10:21
     */
    @EventListener(ContextRefreshedEvent.class)
    public void doEventManager(ContextRefreshedEvent contextRefreshedEvent) {
        final ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        if (applicationContext.getParent()==null){
            //说明是根上下文对象
            System.err.println("容器启动完毕");
            if(applicationContext.getClass().equals(AnnotationConfigApplicationContext.class)){
                //在这里关闭了容器,哈哈哈,我是不是很变态
                ((AnnotationConfigApplicationContext)applicationContext).close();
            }
        }
    }
}
复制代码
  • ContextStartedEvent

在容器启动时触发,即调用start方法,表示所有的Lifecycle Bean都已经显示的接收到了start信号;

  • ContextStoppedEvent

在容器停止时触发,即调用stop方法,表示所有的Lifecycle Bean都已经显示的接收到了stop信号,关闭的容器可以通过start方法启动;

  • ContextClosedEvent

在容器关闭时发布,即调用close方法,表示所有的单例Bean都被销毁,关闭的容器不能被重启或refresh;

  • RequestHandledEvent

再有在使用Spring的DispatcherServlet时有效,即:在一个请求被处理完成时发布;

2.3)事件监听器

Spring中提供了“基于接口”、“基于注解”2种实现监听器的方式;

  • 基于接口

实现ApplicationListener接口,这个接口需要一个泛型对象,这个泛型就是你自定义或者系统定义好的事件对象;实现接口的onApplicationEvent方法;

  • 基于注解

不需要实现任何接口或者类,仅需要在对应的处理方法上添加@EventListener注解即可,这里需要注意的是,方法中仅支持最多一个参数,这个参数类型就是@EventListener注解中配置的事件对象类型,也可以没有任何参数;

以上两种方式效果都是一样的,至于使用那种就看自己的架构设计、习惯及规范了,这里加一句废话:不管是基于接口还是基于注解,你的这个事件监听器都必须能够被Spring扫描并识别,变成托管的Bean;PS:之前我这里有个哥们就是因为这个问题换了各种姿势,调试了一整天都没有成功,还告诉我说发现了Spring的一个重大BUG,非常得意,我哭笑不得。然后。。。就没有然后了;

2.4)工作处理流程

Spring的事件处理机制是观察者模式的一种实现,在Spring中除了发布者与监听者之外,还有EventMultiCaster,这个角色的职责就是把事件转发给对应的监听者;大致的工作处理流程如下:

clipboard.png 在EventMultiCaster中维护着所有的监听器(Listener),在事件发布者发布事件后根据事件的类型匹配并派发信息到对应的事件接受者,由事件接受者自身完成对应的逻辑处理;

三、实现原理

说道原理,肯定要从核心的方法入手了,这里会从org.springframework.context.support.AbstractApplicationContext#refresh方法开始,顺便温习一下之前的内容;如果对内容不太明白,需要参考一下之前的章节;

3.1)准备上下文环境——prepareRefresh方法

/**
 * Prepare this context for refreshing, setting its startup date and
 * active flag as well as performing any initialization of property sources.
 * 准备阶段;是Spring核心方法的第一步,对容器环境做了一些初始化工作
 */
protected void prepareRefresh() {
   // Switch to active.
   this.startupDate = System.currentTimeMillis();
   /*
   * 系统关闭状态设置为:未关闭
   * */
   this.closed.set(false);
   /*
   * 系统活动状态设置为激活状态;
   * 只有在激活状态下才能进行getBean的操作
   * 在本类中的getBean方法中有一句断言容器是否激活的判断方法逻辑:assertBeanFactoryActive方法中对closed及active做了判断
   * 如果active==false的时候会抛出一个异常
   * 如果active==true且closed==true的时候抛出异常
   * 说明如果容器没有启动的时候是不能调用getBean获取Bean的;也就是说要想使用getBean必须先执行refresh();
   * */
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   /*
    * 在网上很多人说该方法没有用,这里还是一样的答案,Spring是一个生态,请不要用一般的眼光去看待他好吗?
    * 就是因为是一个生态级的存在,所以在很多时候考虑的面必须要足够;
    * 这个方法必定是留给个子类实现的;也是留给开发人员的一个可扩展的接口,可以在这里定义一些强制性的规则规范;
    * 比如我们自己写一个类重写了initPropertySources方法,在该方法中设置了一个环境变量的值为A
    * 启动的时候,我的环境变量中没有该值就会启动抛出异常,这样的规范约定,还需要您老人家去一行一行的走查开发人员的代码吗?
    */
   initPropertySources();

   /*
    * 用来校验容器启动必须依赖的环境变量的值
    * 这个方法在通常情况下是与上面的initPropertySources方法联合起来使用
    */
   getEnvironment().validateRequiredProperties();

   /*
    * 创建一个早期事件监听器对象
    * 这里所谓的早期监听器对象就是Spring中用于适应一些自己的事件;
    * 如果你在早期的时间监听器中注册了,那么不需要使用publishEvent方法进行触发;Spring会自动进行发布
    * 这里仅仅是初始化了一个集合,你可以自己添加
    */
   if (this.earlyApplicationListeners == null) {
      this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
   }else {
      // Reset local application listeners to pre-refresh state.
      this.applicationListeners.clear();
      this.applicationListeners.addAll(this.earlyApplicationListeners);
   }

   /*
    * 创建一个容器用于保存早期待发布的事件集合
    * 什么是早期事件了?
    * 就是我们的事件监听器还没有注册到多播器上的时候都称为早期事件
    */
   this.earlyApplicationEvents = new LinkedHashSet<>();
}
复制代码

这里重点就是声明了一个早期的事件监听器和事件,不需要开发人员手动的调用publishEvent方法来发布事件,由Spring来完成发布,那么Spring是在什么时候发布呢?大爷您不要急嘛,慢慢来,往下看;

3.2)Bean工厂的属性填充——prepareBeanFactory方法

/**
 * 为我们的bean工厂填充内部属性
 * @param beanFactory the BeanFactory to configure
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   //设置bean工厂的类加载器为当前application应用的加载器
   beanFactory.setBeanClassLoader(getClassLoader());
   //为bean工厂设置我们标准的SPEL表达式解析器对象StandardBeanExpressionResolver
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
   //为我们的bean工厂设置了一个propertityEditor 属性资源编辑器对象(用于后面的给bean对象赋值使用)
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   /*
   * 注册了一个完整的ApplicationContextAwareProcessor 后置处理器用来处理ApplicationContextAware接口的回调方法
   * 这里需要注意的是Aware这个单词,在Bean的声明周期中,Bean初始化完毕后会调用一堆的Aware,这里就是被调用的Aware之一;
   * 注意:在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
   * 方法中调用initializeBean方法对Bean进行初始化,在这个方法中可以看到仅仅调用了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
   * 那究竟在哪里调用其他的Aware呢?
   * 答案就在下面ApplicationContextAwareProcessor这个对象中,在这个对象中,调用了其他的Aware;有兴趣的可以进入对象中看看;
   * ApplicationContextAwareProcessor是一个后置处理器、后置处理器、后置处理器;重要的事情说三遍;
   * 会在Bean的声明周期中,初始化之前调用;
   * */
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));


   /**
    * 当 Spring 将 Applie础onContextAwareProcessor 注册后,那么在 invokeAwarelnterfaces 方法中间接调用的 Aware 类已经不是普通的 bean 了 ,
    * 如 ResourceLoaderAware、 ApplicationEventPublisher 等,那么当然需要在 Spring 做 bean 的依赖注入的时候忽略它们。
    * 而 ignoreDependencyInterface 的作用正是在此
    */
   beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
   beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
   beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
   beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

   /**
    * 当注册了依赖解析后,例如当注册了对 BeanFactory.class 的解析依赖后,
    * 当 bean 的属性注 入的时候, 一旦检测到属性为 BeanFactory 类型便会将 beanFactory 的实例注入进去。
    */
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);

   /*
   * ***************************划重点***************************
   * 注册一个事件监听器探测器后置处理器
   * ApplicationListenerDetector这个对象会解析接口方式的监听器
   * 在postProcessAfterInitialization方法中进行处理的;
   * 这个后置处理器是在Bean生命周期初始化之后开始执行;
   * */
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // 处理aspectj的
   if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }

   //注册了bean工厂的内部的bean
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      //环境
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      //环境系统属性
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      //系统环境
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}
复制代码

这里特别注意ApplicationListenerDetector这个对象,这个对象是一个Bean的后置处理器(又是后置处理器,Spring的老套路,都是通过这些后置处理器完成的逻辑解耦及集成);

clipboard.png 这个后置处理器是专门用于解析处理接口方式的事件监听器;

3.3)创建多播器——initApplicationEventMulticaster方法

划重点:这个多播器就是Spring用于分发通知的核心对象

/**
 * 从bean工厂中获取或者直接显示的new一个多播器赋值给我们的applicatoinContext对象
 * 采用典型的设计模式就是观察者模式  多播器作为的是一个被观察者
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 */
protected void initApplicationEventMulticaster() {
   //获取我们的bean工厂对象
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   //判断容器中是没有有我们的applicationEventMulticaster 应用多播器组件
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      //直接显示的调用我们的getBean获取出来赋值给我们的applicationContext对象
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isDebugEnabled()) {
         logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
   }
   //容器中没有的话
   else {
      //spring ioc显示的new 一个SimpleApplicationEventMulticaster对象保存在applicatoinContext对象中
      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 + "]");
      }
   }
}
复制代码

在开发中调用publishEvent触发一个事件的时候,就是由这个“EventMulticaster”这个事件多播器负责进行分发通知的;换而言之这个多播器对象中必定保存了所有已知的事件监听器集合;多播器可以理解成事件监听器的“大内总管”;

> 这里提一嘴:所谓的早期时间就是指:onRefresh方法之前(包括此方法)之前注册的事件都是早期事件;

3.4)把事件监听器注册到多播器上——registerListeners方法

划重点:把监听器注册到之前创建的多播器上,也就是把监听器交给大内总管(李总管,我把小陈子交给你了);

protected void registerListeners() {
   /*
   * 获取容器中所有的监听器对象(成品对象)
   * 这个集合里面肯定是空的,除非你调用了annotationConfigApplicationContext.addApplicationListener(【您老人家自定义的监听器】);
   * */
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      //把监听器挨个的注册到我们的多播器上去
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   /*
   * 获取bean定义中的监听器对象
   * Bean定义,Bean定义,Bean定义;重要的事情说三遍;
   * 注意:这里是ApplicationListener类型的监听器;通常来说就是接口类型监听器,故:接口方式的监听器是在这里添加到多播器里的;
   * */
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   //把监听器的名称注册到我们的多播器上
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }

   /*
   * 在这里获取我们的早期事件
   * 这里获得的就是在refresh()方法中第一步prepareRefresh方法执行时注入的
   * */
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   //反手就绝情的清空掉(真TMD绝情),由此可以得出结论:早期事件在org.springframework.context.support.AbstractApplicationContext.refresh方法只会被执行一次
   this.earlyApplicationEvents = null;
   if (earlyEventsToProcess != null) {
      /*
      * 通过多播器进行播发早期事件
      * 这里就是优先推送早期事件的原因
      * */
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}
复制代码

这里有2点需要注意:

  • 1)将接口类型的监听器加入多播器;
  • 2)广播早期注册的事件;

到这里接口类型的监听器是否注册完了呢?呵呵呵呵,答案是否定的,回顾4.2的内容,我们不是在Bean工厂中添加了一个ApplicationListenerDetector类型的后置处理器吗?这哥们就是用来解析接口方式的监听器,是在Bean生命周期的初始化之后执行的;

public Object postProcessAfterInitialization(Object bean, String beanName) {
   /*
   * 如果是ApplicationListener接口的实现就执行下面的处理
   * ApplicationListener就是接口方式需要实现的接口
   * */
   if (bean instanceof ApplicationListener) {
      // potentially not detected as a listener by getBeanNamesForType retrieval
      Boolean flag = this.singletonNames.get(beanName);
      if (Boolean.TRUE.equals(flag)) {
         /*
         * singleton bean (top-level or inner): register on the fly
         * 往多播器中添加这个事件监听器Bean
         * */
         this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
      }
      else if (Boolean.FALSE.equals(flag)) {
         if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
            // inner bean with other scope - can't reliably process events
            logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                  "but is not reachable for event multicasting by its containing ApplicationContext " +
                  "because it does not have singleton scope. Only top-level listener beans are allowed " +
                  "to be of non-singleton scope.");
         }
         this.singletonNames.remove(beanName);
      }
   }
   return bean;
}
复制代码

就是在这里对Bean进行了验证,验证成功后添加到多播器中;

PS:这里如果是我这个变态的话肯定会问:registerListeners添加到了多播器,这里又加一遍是什么意思?是不是Spring的开发人员脑袋抽了? 其实这不是一个问题,反而说明Spring开发人员的严谨;反而是为了解决真正的CRUD在开发业务时脑袋抽了使用“懒加载(@Lazy)”的监听器遗漏而造成的问题。因为懒加载的Bean定义对象是不会在初始化容器的时候创建Bean的,Bean的后置处理器是在Bean的创建过程中调用的;因此在registerListeners中的处理是防止因懒加载而遗漏的问题(懒加载的监听器是通过名称方式添加的);

3.5)注解方式的监听器处理

到了这里肯定有童鞋在问,这里怎么只有接口类型的监听器注入,那注解版本的监听器是在什么时候注入的呢?回顾之前的章节,还记得那个加载创世纪对象的地方吗?(还记得当年清明河畔的夏雨荷吗??);有几个创世纪的对象是专门用于解析注解版的监听器;

注意之前章节讲到的:org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)这个方法中添加了很多创世纪的对象;

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {
   /*
    * ******************************忽略无关的代码******************************
    */
   /*
    * 处理监听方法的注解解析器EventListenerMethodProcessor
    * 专门用于处理@EventListener
    */
   if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
   }

   /*
    * 注册事件监听器工厂
    * 专门用于处理@EventListener
    */
   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;
}
复制代码

clipboard.png 这里的EventListenerMethodProcessor对象是一个Aware,同时注意一下SmartInitializingSingleton这个接口,这个接口会在org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization实例化所有剩余的单例Bean的方法中进行调用,会在所有单例Bean都已经创建完毕之后开始调用; org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

@Override
public void preInstantiateSingletons() throws BeansException {
   /*
    * ******************************忽略无关的代码******************************
    */
   //或有的bean的名称 ...........到这里所有的单实例的bean已经记载到单实例bean到缓存中
   for (String beanName : beanNames) {
      //从单例缓存池中获取所有的对象
      Object singletonInstance = getSingleton(beanName);
      //判断当前的bean是否实现了SmartInitializingSingleton接口
      if (singletonInstance instanceof SmartInitializingSingleton) {
         /*
         * 这里有个创世纪的EventListenerMethodProcessor对象开始执行
         * 用于解析处理@EventListener
         * */
         final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
               smartSingleton.afterSingletonsInstantiated();
               return null;
            }, getAccessControlContext());
         }
         else {
            //触发实例化之后的方法afterSingletonsInstantiated
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}
复制代码

这里又兴趣的童鞋可以进一步跟一下源码;

注意:这里SmartInitializingSingleton同样也是Spring的一个拓展点,注意这个SmartInitializingSingleton调用的时机;

四、异步与同步的事件处理

事件处理在Spring中是支持同步与异步2种模式的,通常情况下是同步执行,如果要进行异步执行的话有2种方式实现;

  • 1)注解方式

在配置类上添加@EnableAsync注解,在监听器的执行方法上添加@Async注解;

  • 2)手动配置方式
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(){
    final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
    //这里如果设置了TaskExecutor就会使用多线程异步的方式进行处理
    simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return simpleApplicationEventMulticaster;
}
复制代码

OK!先到这里吧!这两天怪事怪物比较多,头疼;

文章分类
后端
文章标签