转载

【戏说Spring Boot】Spring Boot监听器解析

文章首发于公众号,欢迎订阅

【戏说Spring Boot】Spring Boot监听器解析

上期问题解答

Q1:谈谈你对 SpringFactoriesLoader 的理解, SpringFactoriesLoader 是如何加载工厂类的?

SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个约定俗成的加载方式,只需要在模块的 META-INF/spring.factories 文件下通过键值对的形式写入,键是接口全限定名,值是以逗号分隔的实现类,使用 SpringFactoriesLoader 来将相应的实现类注册到 Spring 容器中。

加载工厂类的过程:

  1. 查找缓存,缓存存在,直接返回
  2. 缓存不存在,读取指定资源文件
  3. 构造 Properties 对象
  4. 获取 key 对应的 value
  5. 逗号分割 value
  6. 保存结果到缓存

Q2:系统初始化器的作用(结合系统自带的初始化器)

  • DelegatingApplicationContextInitializer

这个初始化器实际上将初始化的工作委托给 context.initializer.classes 环境变量指定的初始化器,这个初始化器的优先级是最高的,因此会被第一个执行。

  • ContextIdApplicationContextInitializer

ApplicationContext 设置一个 Id,这个 Id 由 name 和 index 两个部分组成。

  • ConfigurationWarningsApplicationContextInitializer

用来对常见的由于配置错误而引起的警告进行打印报告。

  • ServerPortInfoApplicationContextInitializer

通过监听 EmbeddedServletContainerInitializedEvent 事件,来对内部服务器实际要监听的端口号进行属性设置。

  • SharedMetadataReaderFactoryContextInitializer

用来创建一个可以在 ConfigurationClassPostProcessor 和 Spring Boot 之间共享的 CachingMetadataReaderFactory

  • AutoConfigurationReportLoggingInitializer

用来将 ConditionEvaluationReport 记录的条件评估详情输出到日志,默认使用 DEBUG 级别,当有异常问题发生时会使用 INFO 级别。

Q3:系统初始化器何时调用?

prepareContext 方法中,会开始调用初始化器进行初始化操作。

Q4:如何实现自定义系统初始化器?

1、在 spring.factories 文件中定义自己的初始化器

2、在配置文件中通过 context.initializer.classes 定义自己的初始化器

3、通过 addListeners() 方法加入将自己的初始化器

Q5:自定义系统初始化器有没有什么注意事项?

注意初始化器的加载顺序。通过两种方式可以修改。

1、通过 @Order() 注解的方式

2、实现 Ordered 接口,重写 getOrder() 方法

下面开始今天的内容!

监听器的注册方法

【戏说Spring Boot】Spring Boot监听器解析

好了,今天我们来说说 Spring Boot 的监听器,其实上一次给大家留了个坑,还记得下面这个构造方法吗?

【戏说Spring Boot】Spring Boot监听器解析

当时我们猜测 Listener 是否也和 Initializer 一样,事实上,注册监听器的过程是一样的,所以说用法和初始化器一模一样,但是我们今天的重点不是监听器的注册过程,而是监听器的实现机制。

不过我觉得还是有必要列举一下监听器的使用。

【戏说Spring Boot】Spring Boot监听器解析

1、定义在 spring.factories 文件中,被 SpringFactoriesLoader 发现注册(工厂加载机制)

【戏说Spring Boot】Spring Boot监听器解析

注意到上面的红框,这里你必须填一个 ApplicationListener 的实现类,告诉 Spring 我只需要监听这一个事件,否则所有内置事件都会被监听到。

配置工厂类

【戏说Spring Boot】Spring Boot监听器解析

2、 SpringApplication 初始化完成后手动添加

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

3、定义成环境变量,被 DelegatingApplicationListener 所发现注册(默认优先级最高)

在配置文件中加入下面一行

context.listener.classes=com.feichaoyu.springboot.initializer.ThirdInitializer

【戏说Spring Boot】Spring Boot监听器解析

上面的实现类都是实现 ApplicationListener 接口,不过还有一个接口 SmartApplicationListener

听这名字就知道很牛逼

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

实现 SmartApplicationListener 接口的监听器可以同时监听多个你感兴趣的事件,而实现 ApplicationListener 接口只能监听一个事件。

【戏说Spring Boot】Spring Boot监听器解析

好了,监听器的使用就介绍到这了,下面开始重头戏了。

引入监听器模型

有请我们的 监听器模型 登场。

【戏说Spring Boot】Spring Boot监听器解析

监听器模型有四要素:

  • 事件
  • 监听器
  • 广播器
  • 事件触发机制

我们通过用户下单的例子来说明这四个要素,这个例子非常重要,是你看懂 Spring Boot 监听器模型的关键。

【戏说Spring Boot】Spring Boot监听器解析

1、定义事件

【戏说Spring Boot】Spring Boot监听器解析

一旦有用户下单,则有一个短信事件,会给用户发送短信。还有一个积分事件,会给用户增加积分。

2、定义事件监听器

【戏说Spring Boot】Spring Boot监听器解析

短信监听器只监听短信事件,积分监听器只监听积分事件,而对其他事件不感兴趣。

3、定义事件广播器

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

首先,在 EventMulticaster 中定义了三个方法,分别是广播任务、添加监听器、移除监听器。

接着, AbstractEventMulticaster 实现了 EventMulticaster 接口,重写了其中的三个方法,同时新增了两个方法,分别是广播前置任务和后置任务。在 multicastEvent 方法中,我们可以看到模板方法的使用,需要子类实现抽象父类。

最后, OrderEventMulticaster 继承了 AbstractEventMulticaster ,重写了两个父类的方法。

事件广播器中包含所有的监听器,会对所有监听器遍历,让监听器找到自己感兴趣的事件。

4、事件触发机制

【戏说Spring Boot】Spring Boot监听器解析

我们还需要把监听器接口的三个实现类、广播器的接口的两个实现类通过 @Component 加入到 Spring 中,我上面没加,大家注意一下。

然后把 AbstractEventMulticasterlisteners 变量加上 @Autowired ,让所有监听器的实现类注入进来。

【戏说Spring Boot】Spring Boot监听器解析

到此,这个例子写完了。

可以测试一下,

【戏说Spring Boot】Spring Boot监听器解析

最终输出结果如下:

【戏说Spring Boot】Spring Boot监听器解析

那么在 Spring Boot 中,监听器的实现会不会也和上面的例子相似,为了证明我们的猜测,不妨去代码中找一下相关的类和接口呗,走你!

【戏说Spring Boot】Spring Boot监听器解析

Spring Boot 监听器模型解析

我们同样也分四要素讨论。

1、事件

我们打开一个之前使用过的 ApplicationStartedEvent 事件类,查看 UML 图

【戏说Spring Boot】Spring Boot监听器解析

从图中我们看出, SpringApplicationEvent 就相当于我们的 OrderEventApplicationStartedEvent 相当于 MessageEventCreditEvent

2、监听器

【戏说Spring Boot】Spring Boot监听器解析

这个接口就相当于我们的 OrderListener 。我们之前通过实现该接口,自定义了一些监听器,Spring Boot 也内置了很多监听器,比如 DelegatingApplicationListener

3、广播器

广播器的层次结构和我们的也是一样的,可以对比来看

【戏说Spring Boot】Spring Boot监听器解析

4、事件触发机制

和我们的 OrderRunListener 类似, EventPublishingRunListener 起到了相同的作用。具体我们下面分析。

【戏说Spring Boot】Spring Boot监听器解析

好了,下面我们开始研究代码。

我们直接定位到这里

【戏说Spring Boot】Spring Boot监听器解析

run 方法点进去,由于方法太长,我分两次截取

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

注意到我所有的红框地方,也就是和监听器打交道的地方都列出来了,接下来我们就来分析一下。

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

进去看下

【戏说Spring Boot】Spring Boot监听器解析

知道为什么要有两个参数了吧,反射构造实例时需要传入的,和类型刚好对上了!

getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); 这行代码很显然就是加载所有实现了 SpringApplicationRunListener 接口的类,不过Spring Boot 中默认只有一个实现类 EventPublishingRunListener

这里我们关注一下 SpringApplicationRunListeners ,这个类是 SpringApplicationRunListener 的集合,从它的定义中也不难发现。

【戏说Spring Boot】Spring Boot监听器解析

它将获取到所有的实现了 SpringApplicationRunListener 接口的类。

OK,我们回到 run 方法,继续向下看

【戏说Spring Boot】Spring Boot监听器解析

兄弟,我要进去了

【戏说Spring Boot】Spring Boot监听器解析

由于默认实现类是 EventPublishingRunListener ,因此我们下一步去它里面看看。

【戏说Spring Boot】Spring Boot监听器解析

用法和我们的相同,都是用过广播器去发布事件的,不过这个 initialMulticaster 是?

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

搜嘎,想起你来了。

那我们继续进去看看吧

【戏说Spring Boot】Spring Boot监听器解析

resolveDefaultEventType 方法就是获取事件的 Class 类型。

【戏说Spring Boot】Spring Boot监听器解析

getApplicationListeners 这个方法值得我们 debug 一下。

【戏说Spring Boot】Spring Boot监听器解析

顺带问下,大家是否好奇这个 source 是啥?

【戏说Spring Boot】Spring Boot监听器解析

接着我们看下 ListenerCacheKey 这个实例。

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

也就是根据 event 类型和 source 类型构造出来的对象。

实际上它是 AbstractApplicationEventMulticaster 的静态内部类。

【戏说Spring Boot】Spring Boot监听器解析

继续向下

【戏说Spring Boot】Spring Boot监听器解析

retrieverCache 变量的定义如下,它的键是 ListenerCacheKey ,值是 ListenerRetriever

【戏说Spring Boot】Spring Boot监听器解析

ListenerRetriever 其实也是 AbstractApplicationEventMulticaster 的内部类,用于存储监听器。

【戏说Spring Boot】Spring Boot监听器解析

第一次是没有缓存的,所以缓存中获取不到监听器,接着我们继续向下走

【戏说Spring Boot】Spring Boot监听器解析

这是个同步方法,我们进入 retrieveApplicationListeners 方法看看。

【戏说Spring Boot】Spring Boot监听器解析

这里就是获取之前添加的 listeners,从 this.defaultRetriever.applicationListeners 中来,那么这个里面的监听器是什么时候添加的呢?

回去找找,不慌!

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

没错,就是这里了,我们之前也说过了,通过 getSpringFactoriesInstances 这个方法会构造出 SpringApplicationRunListener 的实现类,也是说默认的 EventPublishingRunListener ,然后传入参数构造出实例,同时创建一个 SimpleApplicationEventMulticaster 实例,由于监听器已经注册了,所以可以直接获取,把监听器列表加入 defaultRetriever

【戏说Spring Boot】Spring Boot监听器解析

OK,我们回到 retrieveApplicationListeners 方法,继续往下看

【戏说Spring Boot】Spring Boot监听器解析

点进去

【戏说Spring Boot】Spring Boot监听器解析

这里又出现一个类 GenericApplicationListenerAdapter ,从名字可以看出这是一个适配器类,用于将 ApplicationListener 适配成 GenericApplicationListener

【戏说Spring Boot】Spring Boot监听器解析

其中 GenericApplicationListenerAdapter 构造方法中,会采用泛型解析方法 resolveDeclaredEventType 将监听器感兴趣事件解析出来交给 declaredEventType

也就是这一块内容

【戏说Spring Boot】Spring Boot监听器解析

然后判断该监听器是否支持 eventType 事件类型以及 sourceType 源类型。

这里我们点进去看下

【戏说Spring Boot】Spring Boot监听器解析

首先会判断该监听器是否实现了 SmartApplicationListener ,如果是,那么会调用自己重写的 supportsEventType 方法,也就是我们之前写过的

【戏说Spring Boot】Spring Boot监听器解析

只要支持就返回 true,把该监听器加入 allListeners 中,最后返回。

接着回到 getApplicationListeners 方法,将相应监听器列表放入缓存,然后返回所有对指定事件感兴趣的监听器。

【戏说Spring Boot】Spring Boot监听器解析

回到 multicastEvent 方法,开始触发对应监听器的监听事件方法。

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

高能来了!

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

至此,我们分析完了 listeners.starting() 的过程,那么其他过程几乎都是一样的。

还记得我们的 run 方法吗?

【戏说Spring Boot】Spring Boot监听器解析

【戏说Spring Boot】Spring Boot监听器解析

整个红框的方法,就是监听器的整个生命周期。

比如在 prepareEnvironment 时,调用了 listeners.environmentPrepared(environment)

【戏说Spring Boot】Spring Boot监听器解析

后续流程和 starting 时是类似的。

到此我们的监听器机制也分析完了,实际上,Spring Boot 监听器模型用到了 观察者设计模式

【戏说Spring Boot】Spring Boot监听器解析

顺带列举一下 Spring Boot 监听方法与事件的对应关系:

监听方法 对应事件
starting() ApplicationStartingEvent
environmentPrepared(ConfigurableEnvironment environment) ApplicationEnvironmentPreparedEvent
contextPrepared(ConfigurableApplicationContext context) ApplicationContextInitializedEvent
contextLoaded(ConfigurableApplicationContext context) ApplicationPreparedEvent
started(ConfigurableApplicationContext context) ApplicationStartedEvent
running(ConfigurableApplicationContext context) ApplicationReadyEvent
failed(ConfigurableApplicationContext context, Throwable exception) ApplicationFailedEvent

下面又到了你们表演的时候了!

【戏说Spring Boot】Spring Boot监听器解析

思考题

  • Spring Boot 有哪些事件,以及他们的执行顺序?
  • 介绍一下监听器的作用(结合自带的监听器)。
  • 介绍一下 Spring Boot 的事件触发机制。
  • 如何实现自定义系统监听器及注意事项?
  • 说说实现 ApplicationListener 接口和 SmartApplicationListener 接口的区别。

答案我们下期给出!

我创建了一个免费的知识星球,用于分享知识日记,欢迎加入!

【戏说Spring Boot】Spring Boot监听器解析

原文  https://segmentfault.com/a/1190000022438530
正文到此结束
Loading...