述
上文中用一个简单的案例,来了解了一下监听器的实现,下面再来看一下监听器在Spring Boot框架中的实现,围绕以下四个方面来看以下:
- 事件
- 监听器
- 广播器
- 触发机制
监听器
ApplicationListener
这个类,就是系统监听器的实现,代码如下:
这里就是一个函数式接口,然后这里有一个继承自 ApplicationEvent
的泛型,可以在声明的时候,表示对哪种事件感兴趣,然后里面的一个方法就是,监听到感兴趣的事件时,要做的事情.
这个接口跟我们之前demo中的 WeatherListener
是类似的
广播器
ApplicationEventMulticaster
是系统广播器的接口, 看下源码:
这里的几个方法,其实就是添加监听器,移除监听器,广播事件这三种,和我们demo中的 EventMulticaster
也是对应的
事件
Spring Boot 的系统事件,类图如下:
这里一共有 7 个事件,作用分别如下:
ApplicationStartingEvent
:ApplicationStartingEvent
是SpringBoot
启动开始的时候执行的事件,在该事件中可以获取到SpringApplication
对象,可做一些执行前的设置.ApplicationEnvironmentPreparedEvent
: 表示系统环境变量已经创建完成,但是上下文context
还没有创建ApplicationContextInitializedEvent
: 表示ApplicationContext
上下文已经准备好ApplicationPreparedEvent
: 表示 SpringBoot上下文context
创建完成,但此时 Spring 中的 bean 还没有完全加载完成.这里可以将上下文传递出去做一些额外的操作,但是在该监听器中是无法获取自定义 bean 并进行操作的.ApplicationStartedEvent
: 表示所有的bean已经准备完成,但是还没有调用ApplicationRunner
和CommandLineRunner
ApplicationReadyEvent
:ApplicationRunner
和CommandLineRunner
已经完成调用了,也意味着 SpringBoot 加载已经完成.ApplicationFailedEvent
: 容器在启动的时候发生异常的时候发送
事件发发送顺序
ApplicationStartingEvent
-> ApplicationEnvironmentPreparedEvent
-> ApplicationContextInitializedEvent
-> ApplicationPreparedEvent
-> ApplicationStartedEvent
-> ApplicationReadyEvent
监听器注册
Spring Boot中提供了很多监听器,下面来看一下这些监听器是如何被注册到容器中的
类似之前的系统初始化器,我们来看一下源码, 如下:
这里其实和之前的系统初始化器的加载方式是一样的,都是通过 SpringFactoriesLoader
来加载的,点进去看一下源码:
这里和之前的系统初始化器是一样的,就不再详细介绍了
触发机制
上面了解了事件,监听器,广播器,然后四个要素中还有触发机制,这些事件是怎么被发送的,下面来详细看一下
以 starting 事件为例,这个是在框架启动,也就是调用run方法的时候,进入run方法看一下源码,如下:
这里会把所有的监听器都封装到一个 SpringApplicationRunListeners
对象中,这个其实就是 SpringApplicationRunListener
对象的一个集合
进入这个 starting
方法看一下,如下:
可以看到,这里是去调用的 SpringApplicationRunListener
的 starting()
方法,继续往下一级去看,进入 SpringApplicationRunListener
如下:
这里其实就是对所有系统事件的一个封装,在我们之前的demo中,我们是手动创建广播器,然后创建事件发送, 那这里其实就是对这一步进行了一个封装, Spring 容器在运行过程中是不需要构建具体的事件,然后再调用广播器广播, 都是通过这个方法完成的, 以 starting()
方法为例, 我们进入子类看一下:
这里首先找到了广播器,然后发送了一个 ApplicationStartingEvent
事件
案例改造
以之前的天气案例为基础,我们也将代码改造成 Spring Boot 这种方式来试一下
首先,创建一个 WeatherRunListener
对应上面的 SpringApplicationRunListener
, 代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Component
public class WeatherRunListener {
@Autowired
private WeatherEventMulticaster weatherEventMulticaster;
/**
* 发送下雪事件
*/
public void snow(){
weatherEventMulticaster.multicastEvent(new SnowEvent());
}
/**
* 发送下雨事件
*/
public void rain(){
weatherEventMulticaster.multicastEvent(new RainEvent());
}
}
这里需要给 WeatherEventMulticaster
加上 @Component
注解, 同样的 RainListener
和 SnowListener
也需要加上 @Component
注解
然后修改 AbstractEventMulticaster
, 如下:1
2
3
4
5/**
* 监听器的集合
*/
@Autowired
private List<WeatherListener> listeners;
这里只修改存放 WeatherListener
的集合即可, RainListener
和 SnowListener
已经交给了Spring容器去管理,所以这里直接让Spring容器去注入就好了
然后写个单元测试:1
2
3
4
5@Test
public void test1(){
weatherRunListener.rain();
weatherRunListener.snow();
}
执行,控制台输出如下:1
2
3
4
5
62020-02-02 18:28:21.830 INFO 16736 --- [ Test worker] c.z.s.e.m.WeatherEventMulticaster : begin broadcast weather event...
2020-02-02 18:28:21.831 INFO 16736 --- [ Test worker] c.z.s.example.listener.RainListener : Its Rain
2020-02-02 18:28:21.835 INFO 16736 --- [ Test worker] c.z.s.e.m.WeatherEventMulticaster : end broadcast weather event ...
2020-02-02 18:28:21.835 INFO 16736 --- [ Test worker] c.z.s.e.m.WeatherEventMulticaster : begin broadcast weather event...
2020-02-02 18:28:21.836 INFO 16736 --- [ Test worker] c.z.s.example.listener.SnowListener : Its snow
2020-02-02 18:28:21.836 INFO 16736 --- [ Test worker] c.z.s.e.m.WeatherEventMulticaster : end broadcast weather event ...
这种方式就和 Spring Boot 发送事件的方式一样,做了一次封装
事件广播源码
还是以 starting 方法为例,看一下广播器具体是怎么发送事件的
这里点击进入广播器的发送方法
这里的重点是 getApplicationListeners
这个方法,获取对当前事件感兴趣的监听器的集合,点进去往下看
这里主要是从缓存找,然后找不到的话再去计算,计算完成后放入缓存中,供下次使用,再进入具体计算的方法
进入 supportsEvent()
继续看
这里做了一次转换,如果不是 就做个适配 new GenericApplicationListenerAdapter(listener)
,把监听器都转成 GenericApplicationListener
然后做判断
总之这里是要转成一个 GenericApplicationListener
然后再做判断
这里打个断点走一下会更清晰
总结
- 了解监听器是如何注册到 Spring 容器中的
- Spring Boot 中的事件发送机制
- 如何获取事件的所有感兴趣的监听器