ps:真正适合阅读源码的新手来看的SpringBoot源码讲解,如果你真的想读懂SpringBoot源码,可以按照以下推荐的方式来阅读文章
- 打开ide,打开SpringBoot源码,跟着文章一起写注释,写自己的注释
- 不要过于纠结没讲到的地方,毕竟SpringBoot源码那么多,想全讲完是不可能的,只要跟着文章认真阅读,SpringBoot是如何运行的一定可以有一个较为深刻的理解
- 文章适合通篇阅读,不适合跳读,跳跃性的阅读很容易错过重要的东西
- 同样的如果之前的文章没有读过,还是最好先去看之前的文章
- 阅读源码必然少不了大段大段的源码,一定要耐心,不要翻翻了事,往往是那些最长的方法中才是真正需要学习的
- 如果断更了请用点赞、收藏、评论的方式激励我
系列文章链接:
《SpringBoot源码初学者(一):SpringBoot功能扩展接口的使用与源码分析》
一、监听器模式
在学习的路上遵循一些原则,可以更高效的学习,其中就有这么一条“循循渐进”,在深入SpringBoot之前先要了解清楚什么是监听器,监听器是如何实现的,这些都是对付大魔王的神兵利器,和RPG游戏一样打boss之前先要打小怪提升等级,爆出“屠龙宝刀”。
伊泽瑞尔作为瓦罗拉大陆上组名的探险家在探险的路上,却总是受到天气的影响无法冒险,所以他拜托我帮他写一个软件,辅助他关注天气。
1、监听器模式小demo!天气监听器
步骤1:创建抽象类WeatherEvent(天气状态)
1 | 复制代码public abstract class weatherEvent{ |
步骤2:实现下雪和下雨事件
下雪事件
1 | 复制代码public class SnowEvent extends WeatherEvent{ |
下雨事件
1 | 复制代码public class RainEvent extends WeatherEvent{ |
步骤3:创建天气监听器接口
1 | 复制代码public interface WeatherListener{ |
步骤4:实现监听器,分别处理下雪和下雨的天气
下雪的时候需要穿上大棉袄,带上手套御寒
1 | 复制代码public class SnowListener implements WeatherListener{ |
下雨的时候需要带雨伞,穿雨鞋
1 | 复制代码public class RainListener implements WeatherListener{ |
步骤5:创建广播器接口
1 | 复制代码public interface EventMulticaster{ |
步骤6:抽象类实现广播接口
1 | 复制代码public abstract class AbstractEventMulticaster implements EventMulticaster{ |
步骤7:实现天气事件的广播
1 | 复制代码public class WeatherEventMulticaster extends AbstractEventMulticaster{ |
步骤8:测试并触发广播
1 | 复制代码public class Test{ |
2、黑默丁格大讲堂,监听器模式机制讲解
伊泽瑞尔的探险活动终于不再受到天气的骚扰了,可是他并不明白小小的玩意为什么如此神奇,多次询问过我,可是无赖我语言贫乏,无法将如此复杂的思想表达清楚,只要求助老友黑默丁格,帮忙说明。
ps:工作中不仅要能实现功能,还要注重表达能力,在面试的时候能把思想表达的清楚可以拿到更高的薪资,在和测试交流的时候可以帮助测试理解实现原理,测试出隐藏在深处的bug,当然作为天才程序员的大伙是没有bug的,肯定是环境问题或者操作不当导致的。
黑默丁格拿到代码,简单看了两眼就分析出了各个模块的作用:
- 事件:步骤1和步骤2,通过对天气进行抽象,并实现下雨和下雪的天气状态
- 监听器:步骤3和步骤4,规范对天气监听的模式,并且规范对应天气下,需要如何处理
- 广播器:步骤5、步骤6和步骤7,当有事件发生的时候,广播器发出信号,告知所有的监听器,监听器根据事件作出相应的处理。触发下雨事件的时候,下雨监听器收到消息,它抬头一看乌云密布电闪雷鸣,微微一愣,大喊一句:“打雷下雨收衣服啊!!”,广播器继续通知下一个监听器下雪监听器,下雪监听器看看天空,摆摆手,说:“这事与我无关去找别人”
- 触发机制:步骤8,demo中采用的硬编码的形式触发的,在实际运用中,可能是湿度仪检测到湿度暴涨开始下雨了,触发广播。
在23种设计模式中是没有监听器模式的,监听器模式是观察者模式的一种实现,这两个名字都容易让人产生一些误导,在“监听”、“观察”很容易让人觉得是监听器发现了事件,然后行动。实际上是广播器把事件推送给所有的监听器,每个监听器都对事件做出判断和处理。
二、SpringBoot事件监听器的实现
1、ApplicationListener接口
ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制,实现ApplicationListener接口的类,会在SpringBoot加入到广播器中,当ApplicationContext触发了一个事件,就用广播器通知所有实现ApplicationListener接口的类。
1 | 复制代码//这个注解表示,当前类只有一个方法 |
不难发现ApplicationListener的接口与我们实现的天气监听器的步骤3几乎一样,如果理解了小demo这个类的作用肯定已经了解的明明白白。
2、ApplicationEventMulticaster接口
ApplicationEventMulticaster是Spring事件机制的广播器接口,所有的广播器都需要实现此接口,主要作用是管理所有的监听器,以及推送事件给监听器。
1 | 复制代码public interface ApplicationEventMulticaster { |
3、SpringBoot的7大事件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cu6pwkNo-1585491460589)(en-resource://database/2523:1)]
- EventObject:事件顶级对象,所有事件对象的根对象
- ApplicationEvent:应用事件
- SpringApplicationEvent:Spring自己的事件,Spring框架自身的事件都会实现这个接口
- ApplicationStartingEvent:启动事件,框架刚刚启动就会发出这个事件
- ApplicationEnvironmentPreparedEvent:环境在变完成,系统属性和用户指定已经加载完成
- ApplicationContextInitializedEvent:已经创建好了上下文,并且还没有加载任何bean之前发出这个事件
- ApplicationPreparedEvent:在Bean定义开始加载之后,尚未完全加载之前,刷新上下文之前触发
- ApplicationStartedEvent:bean已经创建完成,上下文已经刷新完成,但是ApplicationRunner和CommandLineRunne两个扩展接口并未执行
- ApplicationReadyEvent:ApplicationRunner和CommandLineRunne两个扩展接口执行完成之后触发
- ApplicationFailedEvent:在启动发生异常时触发
(1)事件发生顺序
启动 —》ApplicationStartingEvent —》ApplicationEnvironmentPreparedEvent —》ApplicationContextInitializedEvent —》 ApplicationPreparedEvent —》ApplicationStartedEvent —》 ApplicationReadyEvent —》启动完毕
中间发生异常 —》ApplicationFailedEvent —》启动失败
4、事件监听器的源码分析
(1)监听器注册流程
如果看过之前的文章
《 SpringBoot源码初学者(一):SpringBoot功能扩展接口的使用与源码分析》:https://juejin.cn/post/6844904106843193357
这里就很容易理解,不想完整的阅读可以只看一下工厂加载机制源码解析
的部分
与ApplicationContextInitializer接口完全一样的流程进行注册的,只是把ApplicationContextInitializer接口
换成了ApplicationListener接口
我们还是从最开始的main方法一步步看。
步骤1:查看SpringBoot启动类
1 | 复制代码@SpringBootApplication |
步骤2:这里可以看到一层简单的调用
1 | 复制代码public static ConfigurableApplicationContext run(Class<?> primarySource, |
步骤3:这里就比较有意思了,注意一下注释
1 | 复制代码public static ConfigurableApplicationContext run(Class<?>[] primarySources, |
步骤4:没有什么用的封装,对构成函数复用
1 | 复制代码public SpringApplication(Class<?>... primarySources) { |
步骤5:
这里我们可以看到两个熟悉的名字getSpringFactoriesInstances方法和ApplicationContextInitializer接口
1 | 复制代码public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { |
(2)监听器触发流程
步骤1:查看SpringBoot启动类
1 | 复制代码@SpringBootApplication |
步骤2:ConfigurableApplicationContext类
1 | 复制代码public static ConfigurableApplicationContext run(Class<?> primarySource, |
步骤3:这次进入run方法
1 | 复制代码public static ConfigurableApplicationContext run(Class<?>[] primarySources, |
步骤4:每次看到这个方法,都感觉它罪孽深重,多少人从它开始看起,踏上阅读源码的不归路
代码较长,这次就不写所有的注释了,具体注释看这里https://juejin.cn/post/6844904106843193357
1 | 复制代码 public ConfigurableApplicationContext run(String... args) { |
步骤5:没有千层套路
1 | 复制代码public void starting() { |
步骤6:广播器发送事件
1 | 复制代码@Override |
步骤7:广播器发送事件
1 | 复制代码@Override |
步骤8:广播事件的时候要判断这个事件的类型,判断需不需要在这个时间点执行
1 | 复制代码@Override |
步骤9:获取事件类型
1 | 复制代码private ResolvableType resolveDefaultEventType(ApplicationEvent event) { |
步骤10:通过接口判断时间类型
1 | 复制代码public static ResolvableType forInstance(Object instance) { |
步骤11:
开始广播
两个参数:event:需要执行的事件 eventType:事件的类型
1 | 复制代码@Override |
步骤12:
获取对这个事件感兴趣的监听器(缓存获取逻辑)
参数说明:
event:当前发生的事件,这个方法就是找到对这个事件感兴趣的监听器
eventType:事件类型
1 | 复制代码protected Collection<ApplicationListener<?>> getApplicationListeners( |
步骤13
:真正获取监听器的逻辑
1 | 复制代码private Collection<ApplicationListener<?>> retrieveApplicationListeners( |
步骤14
:判断监听器是否对当前事件感兴趣
1 | 复制代码protected boolean supportsEvent( |
5、自定义SpringBoot监听器
(1)通过spring.factories注入
步骤1:创建监听器,并实现ApplicationListener接口
1 | 复制代码//我们让这个监听器对ApplicationStartedEvent事件感兴趣 |
步骤2:在spring.factories中添加实现类的指引
这里涉及上一讲的内容,还不会的小伙伴们猛戳这里,赶紧补习一下:https://juejin.cn/post/6844904106843193357
1 | 复制代码#com.gyx.test.Listener是刚刚写的监听器的全路径名 |
然后运行程序,就可以发现打印的语句出现了
(2)SpringApplication手动注入
步骤1:创建监听器,并实现ApplicationListener接口,和上面的完全一样
步骤2:修改SpringBoot启动类
1 | 复制代码@SpringBootApplication |
(3)SpringBoot的配置文件中注册
步骤1:创建监听器,并实现ApplicationListener接口,和上面的完全一样
步骤2:修改配置文件
1 | 复制代码context.listener.classes=com.gyx.test.TestListener |
看过上一课的小伙伴们,是不是发现了,和之前ApplicationContextInitializer的注册方式完全一样!!!是不是有点感觉了,趁热打铁赶紧吧上一讲再去回顾一下吧
(4)多事件监听,实现SmartApplicationListener接口
这种方法只是实现的接口不一样,注入的方式是一样的,上面的三种注入方式都可以使用
步骤1:创建监听器,并实现SmartApplicationListener接口
1 | 复制代码@Order(1) |
本文转载自: 掘金