中的listeners_一文看懂SpringBoot中的Event and Listener

前言:

最近一直在看SpringBoot的官方文档,也一直考虑要不要写个文章,本来还是挺纠结的,害怕内容有误,误人子弟,但是文档读过去,自己动手操作一遍,当时感觉理解还行,过了一段时间又忘记了(这是第二遍看文档),所以决定还是写个文章吧,就当自己的学习笔记了,如果文中出现理解上的偏差,烦请各位大佬指正。

1.SpringBoot中加载自定义Listener的方式

  1. 第一次接触SpringBoot的时候,觉得这个东西太简单,首先简单的是他的启动方式,就是一个main函数,其实就是一行代码:
SpringApplication.run(Class<?> cl,args);

我们自定义的listener,有些触发是在ApplicationContext refresh前,有些是在ApplicationContext refresh后(关于哪些在前,哪些在后,我们后面再讨论),我们可以改造一下我们的main函数,在服务启动的时候,把我们的listener交给Spring,Spring会按照顺序帮我们加载并启动,如下:

SpringApplication application = new SpringApplication(Class<?> cl);
application .addListeners( new ApplicationReadyListener() );
application .addListeners( new ApplicationFailedListener() );
application .run(args);

这样我们就把我们自定义的listener交给Spring去处理了,至于Spring是这么是处理的,我们看一下Spring的源码,我们首先进入run方法,如下:

717652ad7c6e91f06f51f4c7193df6ad.png
SpringApplication run方法源码

getRunListeners()这个方法我们后面再说,先进入listeners.starting()这个方法看一下:

65377d507641f42b57549cf8812ab12c.png

这个方法里面,就启动了我们的所有的listener,通过这种方式我们就将我们的listener交给spring管理了,我们只要关注我们listener内部的逻辑就可以了。这种方式还有一种变种,如果我们的应用程序有父子等级之分,我们就需要一种链式的方式来启动,如下:

SpringApplicationBuilder builder = new SpringApplicationBuilder();
builder.listeners(new BlogApplicationContextInitializedListener(),new BlogApplicationPreparedListener());
builder.run(args);

上述代码段,只演示如何添加listener,这种方式注册的listener当spring进行publish的时候,会在我们的父子层分别publish两个名字一样的listener,如何区分呢,我们可以在父子层分别实现ApplicationContextAware,注入对应的applicationContext,进行区分就行了。

2.如果我们想我们的listener自动注入到spring中,我们再看一下,我们上面说到的run()方法中的getRunListeners(args)方法,我们进去看一下源代码:

69a221c7a2a2d7ed7241845f2313ce4a.png

我们主要看一下getSpringFactoriesInstances这个方法:

d67727d99f35f1940f288f5bbbda1b1d.png

这个方法有两个关键点:

  1. SpringFactoriesLoader.loadFactoryNames()这个方法主要的作用就是去我们的claasspath路径下找/META-INF/spring.factories这个文件中对应type下的value值,也就是我们定义的listener格式如下:

c3bb220488420baf93a98f39cd2ce9b9.png

2.这个方法找到所有的listener的类名,然后通过createSpringFactoriesInstances()这个方法创建对应的Instance,主要通过反射的机制来创建对象,listener实例创建好后,就和第一步中的方法一样了,启动所有的listener。上面介绍了三种方式可以将我们自定义的listener交给spring去管理,并正确的启动。

2.SpringBoot中的Envent

  1. ApplicationStartingEvent在所有的处理线程执行之前执行,除了listener的实例化和加载
  2. ApplicationEnvironmentPreparedEvent 在context被创建之前,当Environment被context使用的时候触发执行
  3. ApplicationContextInitializedEvent在ApplicationContext准备好,并且 ApplicationContextInitializers被正确的调用了,虽然ApplicationContext已经准备好了但是Bean还没有初始化
  4. ApplicationPreparedEvent在context刷新之前,但是BeanDefinition被加载后触发,我们都知道spring初始化Bean的第一步就是初始化对应definition,所以这个阶段Bean依旧没有初始化
  5. ApplicationStartedEvent在context刷新后触发,这个时候Spring的容器已经被初始化好了,我们需要的Bean等都已经被正确的初始化了
  6. ApplicationReadyEvent上面容器被正确的初始化后,接下来就是启动我们的中间件容器了,比如tomcat,Tomcat启动后就会触发此事件
  7. 下面介绍两个事件在 ApplicationStartedEvent之后触发,在ApplicationReadyEvent之前触发
    1. ContextRefreshedEventcontext被刷新后执行
    2. WebServerInitializedEventWebServer正确启动后触发,如果是web服务则是ServletWebServerInitializedEvent,如果是reactive则是ReactiveWebServerInitializedEvent

SpringBoot会在启动的时候,按照上面的顺序触发对应的event,如果我们有注册进Spring中, 上面的event我都写了个listener,下面是我执行的结果:

29bfe853f07f5807912d083144325b83.png

0d03b7e1c06ec9e4068cdfc8dde790de.png

上面的截图,可以看出每个event的执行顺序和我们上面的说的执行顺序是一致的,大家可以自己写一个debug一下,按照我这个顺序看对应的源码,就能很清晰的在哪个阶段触发哪个event,这样我们如果业务有需求,就可以做一些定制化的开发,关于SpringBoot中的Event和Listener就先介绍这么多,其实挺简单的,如有不妥的地方,望指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值