Spring Boot监听器解析

监听器的注册方法

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

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXwTzKeL-1587606340219)()]

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

配置工厂类

image-20200420153244114

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

image-20200420153740235

image-20200420153813014

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

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

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

image-20200420154359892

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

听这名字就知道很牛逼

image-20200420160608508

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

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

引入监听器模型

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

监听器模型有四要素:

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

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

在这里插入图片描述

1、定义事件

image-20200422210852059

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

2、定义事件监听器

在这里插入图片描述

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

3、定义事件广播器

image-20200422210949453

image-20200422211006632

image-20200422211018168

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

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

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

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

4、事件触发机制

image-20200422211033262

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

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

在这里插入图片描述

到此,这个例子写完了。

可以测试一下,

image-20200421153101558

最终输出结果如下:

image-20200420215907819

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

Spring Boot 监听器模型解析

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

1、事件

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

在这里插入图片描述

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

2、监听器

image-20200421152913729

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

3、广播器

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

image-20200421154049172

4、事件触发机制

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

在这里插入图片描述

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

我们直接定位到这里

image-20200421212910950

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

image-20200421213143136

image-20200421213244867

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

在这里插入图片描述

进去看下

image-20200421213750272

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

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

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

image-20200421214949406

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

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

image-20200421215141816

兄弟,我要进去了

image-20200421215333165

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

在这里插入图片描述

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

在这里插入图片描述

image-20200421215953065

搜嘎,想起你来了。

那我们继续进去看看吧

image-20200421222130451

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

image-20200421222525999

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

在这里插入图片描述

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

在这里插入图片描述

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

image-20200421225144546

在这里插入图片描述

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

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

image-20200421225502738

继续向下

image-20200421225948358

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

image-20200421230011961

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

image-20200421230302122

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

image-20200422113141459

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

image-20200422115746260

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

回去找找,不慌!

image-20200422120211155

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

image-20200422122324502

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

image-20200422122913812

点进去

image-20200422123158989

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

image-20200422150259564

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

也就是这一块内容

image-20200422150512977

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

这里我们点进去看下

image-20200422145731448

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

在这里插入图片描述

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

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

image-20200422143250164

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

image-20200422143636912

image-20200422143809788

高能来了!

在这里插入图片描述

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

还记得我们的 run 方法吗?

image-20200421213143136

image-20200421213244867

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

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

在这里插入图片描述

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

到此我们的监听器机制也分析完了,实际上,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
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值