乐于分享
好东西不私藏

Spring——事件之源码

Spring——事件之源码

    每天进步一点点,大家好,我是大龄码农。

    今天我们聊聊事件的源码。

    大家是否还记得refresh()方法,一个非常重要的方法,在AbstractApplicationContext类中。

    说明:今天要着重介绍的,就是被框出来的这三个方法

    一、initApplicationEventMulticaster()

    说明:这个方法比较简单,查找是否有名为“applicationEventMulticaster”的bean,如果有就使用自定义的,如果没有就使用默认的SimpleApplicationEventMulticaster类,并手工注册到Spring容器中

    说明:这是继承关系图,对于类中的属性和方法,在后面分析时再说

    二、registerListeners()

    说明:第一段代码是获取容器中所有的监听器对象并添加到事件广播器中,第二段代码是获取所有类型为ApplicationListener的bean名单并添加到事件广播器中,第三段代码是获取早期事件并发布

    在讲finishRefresh()方法之前,我们再说一下在finishBeanFactoryInitialization()这个非常重要的方法中发生了什么?其实就是后置处理器又搞了哪些事情。

    1、ApplicationListenerDetector

    说明:这是专门用于处理ApplicationListener的,实现了BeanPostProcessor接口

    说明:将ApplicationListener添加到容器中

    说明:广播器将listener添加到其内部类DefaultListenerRetriever中

    2、EventListenerMethodProcessor

    说明:这是专门用于处理@EventListener注解的。

  • 实现BeanFactoryPostProcessor接口的postProcessBeanFactory()方法

    说明1:EventListenerFactory有两个实现类,一个是DefaultEventListenerFactory,对应处理@EventListener注解,另一个是TransactionalEventListenerFactory,对应处理@TransactionalEventListener注解。

    说明2:由于@TransactionalEventListener注解更特殊,所以通过排序将其放在第一个去匹配处理对应的方法

  • 实现SmartInitializingSingleton接口的afterSingletonsInstantiated()方法

    其中,调用了processBean()方法

    说明:筛选出@EventListener注解的方法

    说明1:如果方法不为空时执行这部分逻辑,对于红框部分的逻辑,如果factory的类型是DefaultEventListenerFactory时,会创建ApplicationListenerMethodAdapter对象,如果factory的类型是TransactionalEventListenerFactory时,会创建TransactionalApplicationListenerMethodAdapter对象。

    说明2:将注解对应的监听器对象存入到容器中

    三、finishRefresh()

    说明:这里发布了一个ContextRefreshedEvent事件

    说明:这里主要就是初始化事件并广播

    说明:查到符合条件的监听器并调用对应的onApplicationEvent()方法。这里主要看下getApplicationListeners()方法中的retrieveApplicationListeners()方法

    说明1:初始化监听器(类型为ApplicationListener的bean以及被@EventListener注解转化为XXXMethodAdapter对象的bean)和监听器的bean名单(类型为ApplicationListener的bean)

    说明2:将匹配对应事件类型的监听器存入到待返回的监听器中

    说明1:针对监听器的bean名单中的bean再做一次判断,检查其是否支持对应的事件,如果不支持就从待返回的监听器中移出。

    说明2:这部分代码在一定程度上,会与已注册的监听器有部分重叠。

    源码分析到这里,我们回头看下Spring事件中的问题

问题:为什么使用ApplicationContext能自动监听到事件,而使用自定义的事件发布器就不能自动监听到事件呢?

回答:答案很简单,就是缺少了multicastEvent()这个步骤,因为在这个方法中,会获取所有符合条件的监听器,并调用其处理事件的方法。所以通过手动调用SimpleApplicationEventMulticaster类的multicastEvent()方法,问题得到解决。

最后,再说下线程池,刚才在源码分析中大家也看到了Executor类,默认是null,因为@EventListener注解默认是在发布事件的线程上同步执行监听器方法,也就是串行化执行。如果想要并发执行,就要指定线程池。这里有两种实现方式:

  • 自定义ApplicationEventMulticaster

    从源码得知,SimpleApplicationEventMulticaster这个类是支持监听器异步调用的,但是每次通过context获取bean后再设置taskExecutor属性,总觉得怪怪的。不如索性自定义ApplicationEventMulticaster,并实现这部分逻辑。

    定义线程池

    说明:修改线程前缀,这样能看出差异

    自定义ApplicationEventMulticaster

    说明:注意bean的名称只能是applicationEventMulticaster。

    我们在原有代码的基础之上,增加关于线程的打印信息,测试结果如下

    说明:main线程串行执行

    增加自定义ApplicationEventMulticaster后,测试结果如下

    说明:指定线程池,监听器并发执行

注意:这里所有的监听器都变成了异步执行

  • 使用@Async注解

    1、使用@EnableAsync注解标识支持异步

        一般在启动类上标注

    2、使用@Async注解标明要使用的线程池

        这里在NewArticleListenerB类的onListenEvent()方法上增加@Async注解

    测试结果

    说明:@Async注解可以让指定的监听器异步执行,其他的监听器依然串行执行

    总结:
    1、介绍了事件源码中实现ApplicationListener类的处理方式
    2、介绍了事件源码中使用@EventListener注解的处理方式
    3、介绍了异步执行监听器的方法并用程序讲解