SpringBoot项目启动过程源码终于整体捋了一遍(六)

截止上篇,关于SpingBoot的启动流程终于看完了如何初始化SpringApplication,内容还是挺多的足足写了5篇。并且过程中遗留了一个问题,即初始化SpringApplication的时候设置初始化器setInitializers()和设置监听器setListeners()在启动流程中起着什么作用。

带着遗留问题这篇开始看初始化完SpringApplication之后的run方法,先贴图:

嚯,内容挺多的,这里可以发现run()方法是待着参数args的,还记得当初只是想知道args和java -jar的参数是什么关系,离答案应该不远了。

首先看一二行:

 StopWatch一般用于计时,有start()后面必然有stop():

 然后用该类的getTotalTimeSeconds()方法可以拿到期间运行总时间:

看来我们平时程序中需要计时的时候不要再用写两个System.currentTimeMillis()一减了,这样更优雅一点,而且StopWatch类中还有其他方法提供了更多的功能,可以去看一下学起来。

接着往下看:

前面先是定义了一个 ConfigurableApplicationContext类,这也是run()方法的返回值,然后是定义了一个异常报告的集合?行吧,定义好了就先放着吧,有什么用后面再看。这个configureHeadlessProperty()乍一看不知道干嘛的,可以看一下这个方法:

设置系统属性,这个属性是这样子的:

Headless模式是系统的一种配置模式,在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。我们的应用一般跑着服务器上的嘛,这哪来的外设,所以设置为Headless模式也是应该的。

回到run()方法继续往下看:

 先来看一下这个SpringApplicationRunListeners类是什么,因为后面还调用了它的starting()方法:

看来这个类就是个干流水线活的,它的starting()、 environmentPrepared()包括其他方法都是遍历这个监听集合listeners,一个一个的调用一遍里面元素SpringApplicationRunListener的对应方法,少了个s,就上了流水线。回到run()方法,看来关键是getRunListeners(args)方法拿到了哪些SpringApplicationRunListener应用运行监听,再看这些监听是干什么的,看一下这个getRunListeners()方法:

 初始化SpringApplicationRunListeners流水线的时候又看到了这个getSpringFactoriesInstances(),这个方法在第四篇(https://blog.csdn.net/weixin_42447959/article/details/105065584)分析过,就是加载在Spring.factories里配置好的类嘛,这里是取出键为SpringApplicationRunListener的类。但是这里调用这个方法的时候好像参数变多了,有type还有args,第四篇分析设置初始化器和设置监听器的时候调用这个方法这两个参数都为空,不得不再看一遍这个方法:

这边再看就主要关注parameterTypes和args这个两个参数有什么用了,和之前的关注点不一样,也没有白看,直接看createSpringFactoriesInstances()方法,看来这两个参数是创建示例时候用的,记着这里的parameterTypes是一个Class数组,里面有SpringApplication.class和String[].class两个元素,而args就是启动命令里面输入的字符串了。看一下方法里面:

大致可以看出来parameterTypes是为了拿构造器,而args只是构造实例的时候用的,并没有用它去干吗,就不想往下追究了,其实这时候可以猜测一下拿到的那些SpringApplicationRunListener应该有一个属性是args,去验证一下就行了,通过IDEA中类继承实现拓扑图功能可以看到SpringApplicationRunListener类只有一个实现类EventPublishingRunListener,在spring.factories中也可以得到验证:

看一下这个类:

 果然有一个属性args,并且SpringApplication也是它的属性。到这里需要把前面的理一下,从getRunListeners(args)拿到了这个SpringApplicationRunListeners流水线公司开始理,它里面的各种方法都是流水线作业,遍历调用真正的应用运行监听SpringApplicationRunListener的对应方法,所以重点应该是SpringApplicationRunListener里面各种方法干了什么,现在SpringApplicationRunListener接口在spring.factories中只配置了一个实现类即EventPublishingRunListener,所以在重点又成了这个里面的starting()等各种方法是在干什么,流水线上执行的就是他们,看了一眼,这里面好多方法,而且又是args又是SpringApplication的,作为流水线上的真正且唯一的工人肯定干了好多活,那就仔细看一下这个应用运行监听。

先看一下EventPublishingRunListener的构造方法:

先是把SpringApplication和args初始化为自己属性,还初始化了一个SimpleApplicationEventMulticaster类,这是一个应用事件分发器,紧接着就是通过application.getListeners()拿到了之前初始化SpringApplication的时候设置的监听器,并且SimpleApplicationEventMulticaster类的addApplicationListener()方法,就是告诉事件分发器有这么多应用监听。

看到这里是又高兴又不高兴,高兴的是之前遗留的问题解决了一半,终于知道初始化SpringApplication的时候设置的监听器有什么用了,不高兴的是哪来的这么多listener监听听来听去的啊,这里可以理一下:

可以看出初始化SpringApplication的时候设置监听用的是AppliactionListener接口,将其理解为应用监听,实现类拿了一大堆。而后者在run()方法中拿应用运行监听用的是SpringApplicationRunListener接口,将其理解为应用运行监听且只有一个实现类即EventPublishingRunListener,初始化该类的时候又需要用到前者AppliactionListener接口的一大堆实现类,即初始化应用运行监听的时候需要拿到之前那一大堆的应用监听,这就有点好理解了,应用运行监听SpringApplicationRunListener就一个,里面真正干活的其实是这些应用监听AppliactionListener,其实都是根据类名直译过来的。

前面还说SpringApplicationRunListeners是流水线,SpringApplicationRunListener是真正工人呢,现在看来这个SpringApplicationRunListener也不是个干活的工人,它就是个拉皮条的,它有一个SimpleApplicationEventMulticaster应用事件分发器可以让真正的工人AppliactionListener应用监听都干起活来。

看完了EventPublishingRunListener的构造方法,就可以看看下面是怎么通过一票的方法让真正的工人AppliactionListener应用监听干不同的活,一个一个看。

先是starting()方法,听着像是启动应用的活,看一下:

可以看到先是利用SpringApplication和args构造了一个ApplicationStartingEvent对象,即应用启动事件对象,看一下这个ApplicationStartingEvent类的继承拓扑图:

可以发现ApplicationStartingEvent应用启动事件是没有属性的,args是父类SpringApplicationEvent的属性,而SpringApplication则是在往上的EventObject的一个属性(source)。

继续往下看然后是调用了应用事件分发器的multicastEvent()方法,即分发事件方法,参数就是构建好的ApplicationStartingEvent对象,可以看一下这个方法:

这是一个重载方法,可以看到先是调用resolveDefaultEventType()方法拿到事件类型ResolvableType,不禁好奇这个事件类型是根据什么定的,这次的事件是ApplicationStartingEvent应用启动事件,继续往下看拿到的ResolvableType是什么,看这个resolveDefaultEventType()方法:

就这?好吧直接看forInstance()方法:

这次调用的instance是ApplicationStartingEvent显然不是ResolvableTypeProvider类型,直接看后面的forClass方法,参数是ApplicationStartingEvent的Class类型:

可以看到是根据class类型构建了一个ResolvableType对象,大佬们能不能直接一点,那就继续看一下怎么构造这个ResolvableType的吧:

意思就是事件类型type就是参数class类型,举个例子,这次的ApplicationStartingEvent应用启动事件类型就是ApplicationStartingEvent.class,就这?这么一顿截图,大佬们也太含蓄了,好吧,好歹理清楚了继续玩下看,前面的图再贴一遍:

所以这次调用的事件是ApplicationStartingEvent,事件类型的type是ApplicationStartingEvent.class,然后调用了重载的multicastEvent()方法,方法一进来还有确认了一下事件类型ResolvableType对象不为空,否则再去解析一遍事件类型,看来这个事件类型type挺重要的,看后面怎么用吧。然后是通过getTaskExecutor()拿到了线程池Executor对象,线程池嘛,这里就不看怎么拿的了。

接下来是一个重点,有一个for遍历,注意元素是ApplicationListener应用监听,如果混淆了去前面理一理,这是真正干活的工人,也是最初初始化SpringApplication的时候从spring.factories文件中加载进来的即是否还记得setListeners(),EventPublishingRunListener这个拉皮条的调用了SimpleApplicationEventMulticaster应用事件分发器的multicastEvent()分发事件方法,让真正的工人AppliactionListener应用监听都干起活来,看,这里开始把工人一个一个的逮出来了干活了。可是前面说到初始化EventPublishingRunListener的时候不是已经从SpringApplication里面拿到所有的ApplicationListener了吗,为什么不直接遍历而是调用了getApplicationListeners(event, type)方法,根据事件和事件类型又去筛选了一遍拿到了当前事件和事件类型的AppliactionListener应用监听,看来让工人干活之前还要挑一挑符合要求的,工人也是分工种的嘛,这个方法再往下看有点写不完了,举例直接说结论吧,比如看我们熟悉的SqlSessionFactoryBean类:

没想到吧它也是个干活的ApplicationListener应用监听,注意泛型是ApplicationEvent,还记得刚刚的getApplicationListeners(event, type)方法不?如果这里的event是ApplicationEvent类型,这个应用监听就会被拿到,即这个工人专业对口要被抓去干活了,上面的继承关系拓扑图里可以看出这个ApplicationEvent是一个父类,那岂不是很容易就被抓取干活去。再举个例子,这儿有一个自定义的类:

 即如果分发的事件类型是ApplicationEnvironmentPreparedEvent,那这个应用监听就要去干活了。

继续往下看筛选后拿到所有的专业对口的ApplicationListener应用监听后,遍历调用了invokeListener()方法,这个方法图就不贴了,里面调用了doInvokeListener(),直接看这个方法:

果然,这里调用了ApplicationListener的onApplicationEvent()方法,执行每个应用监听真正的逻辑。

 每个ApplicationListener的实现类都会去重写这个onApplicationEvent()方法,以完成自己的逻辑。

源码撸到这里整个过程已经清晰了,可以在理一遍,就从SpringApplicationRunListeners的starting()方法开始:

这个SpringApplicationRunListeners里面有一个SpringApplicationRunListener集合,它的starting()方法其实就是遍历调用集合中SpringApplicationRunListener元素的starting()方法,SpringApplicationRunListener只有一个从spring.factories里读出来的实现类叫EventPublishingRunListener,它里面有一个事件分发器SimpleApplicationEventMulticaster,事件分发器有一个分发事件的方法multicastEvent(),这个方法的参数是构建好的不同的事件,例如starting()方法中构建的就是ApplicationStartingEvent应用启动事件。方法里面的逻辑是根据参数事件的事件类型筛选出所有符合事件类型的ApplicationListener应用监听,然后遍历执行它们重写的onApplicationEvent()方法以执行各自的逻辑。理解起来就是ApplicationListener应用监听成功监听到了事件发布器发布的事件,然后执行了自己的逻辑,大佬们给类这么起名字估计就是想让我们这么理解,然后撸源码的过程中可没有什么监听不监听的骚操作,就是在一堆ApplicationListener里挑一挑而已,主动的动作在代码里写出来咋就成被动的呢?

这是starting()方法,他还有environmentPrepared()等方法也都是这样,只不过不同的方法里调用multicastEvent()分发了不同的事件,可以看图:

看方法名貌似SpringBoot启动时通过这种事件分发机制干了好多大事,应用环境准备?上下文准备?上下文加载?这些方法后面应该会调用,重点就在spring.factories里面配置了哪些ApplicationListener的实现类,这些应用监听会在这几个方法中被拿到执行onApplicationEvent()方法中自己的逻辑,这篇看了starting()方法,打断点看了一下这些ApplicationListener的实现类被拿到了:

这是starting()方法里面监听到事件的监听器的,其他方法之后再说,至于这些监听器各自做了什么,我看了一眼没有用到参数args,这里就不展开说了,可以去看一看。

这篇就到这里了,例常总结一下,这篇看到了SpringApplicationRunListeners的starting()方法,发现了SpringBoot启动时候借助这种事件分发机制做了很多事,具体哪些事得看spring.factories里面配置了哪些ApplicationListener的实现类。并且遗留问题解决了一半,SpringApplication中设置的监听器就是在这里用到了,但是设置的初始化器还不知道在哪里用到,这个问题仍然遗留着。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值