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

上篇遗留了一个问题,即初始化SpringApplication时设置初始化器setInitializers()有什么用。上篇依然是没有看完run()方法,先贴一下run()方法:

 上篇看到了打印Banner,继续往下看:

 ConfigurableApplicationContext是run()方法的返回值,看来是在这个方法里初始化的,看一下这个方法:

又是分应用类型,之前遇到过不少次了,看来是不同的应用类型构建了对应的ConfigurableApplicationContext,可以看一下都有哪几种:

这里构建的是AnnotationConfigServletWebServerApplicationContext,名字怪长的。注意它是用BeanUtils.instantiateClass()去构造对象的,这个方法是用类的默认构造函数,即无参构造函数去创建对象,平时如果需要可以学起来。

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

这个异常报告不想去关注太多,可以看到它也是通过getSpringFactoriesInstances()拿到实例对象的,之后在catch到异常后打印异常是需要用到这个exceptionReporters,这里就不多看了。

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

哈,这是一个context三联,即准备、刷新以及刷新后,一个一个看一下,先看准备context的方法prepareContext():

这个方法也太贪心了吧,参数里上下文context、环境environment、可以发布事件的listeners以及应用参数applicationArguments居然都齐全了,居然最后还带上了Bannert打印的printedBanner,敢情前面做的工作这里一点都不浪费,方法一上来先是将环境environment对象set给上下文context,然后调用了一个前置方法postProcessApplicationContext(),看一下这个方法:

先是保证上下文context是单例的,然后给它setResourceLoader资源加载器,这个this.resourceLoader在初始化SpringApplication的时候就初始化过,第二篇提到过(https://blog.youkuaiyun.com/weixin_42447959/article/details/104943589),然后setClassLoader类加载器,然后setConversionService类型转换器,这个类型转化器第七篇提到过(https://blog.youkuaiyun.com/weixin_42447959/article/details/105289475),总结起来就是给上下文context一顿set,肥肥的。

回到prepareContext()方法继续往下看,调用完前置方法后,调用了applyInitializers()方法,还记得刚刚调用了BeanUtils.instantiateClass()去构造上下文context对象,这是调用的无参构造函数,似乎这里开始初始化上下文context对象的参数了,看下这个applyInitializers()方法:

 哈,getInitializers(),终于看到初始化SpringApplication时设置初始化器setInitializers()在哪里get出来了,这里首先拿到所有的初始化器做了一个循环遍历,里面重点是用Assert.isInstanceOf()方法检查上下文conetext是不是已经初始化过了,即是不是一个实例,这里当然是了。然后是执行各个初始化器的initialize()方法,举个初始化器的例子看看他们的initialize()方法都在干什么,比如这个ServletContextApplicationContextInitializer:

好吧,又是拿到上下文context一顿set,系统中初始化器还不少,这里就不多看了。

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

这已经很熟悉了,最终是发布了ApplicationContextInitializedEvent事件,事件发布机制第六篇分析过(https://blog.youkuaiyun.com/weixin_42447959/article/details/105263684)。

继续往下看:

这是记录启动日志相关。

继续往下看:

 

 有一行注释意思应该是添加SpringBoot特殊的单例bean,这里应该是指ApplicationArguments应用参数和Banner打印图标两个对象,这两个对象在前面是new出来的,没有印象的话可以翻翻前面,这里贴出来:

我们知道new出来的对象是没有添加进Bean容器的,这里是拿到beanFactory后用beanFactory.registerSingleton手动将对象交给bean容器管理,这个方法可以学起来,用处挺多的。最后setAllowBeanDefinitionOverriding是设置bean容器中是否允许同名bean覆盖,this.allowBeanDefinitionOverriding是个布尔型变量,可以自己set设置。

继续往下看:

getAllSources()方法还记得不,拿到所有的主源类,一般即启动类,这个第二篇提到过(https://blog.youkuaiyun.com/weixin_42447959/article/details/104943589) 。然后是调用load(),最后又开始发布事件了,这里发布的是ApplicationPreparedEvent事件,这就不看了,看一下load()方法:

这里用主源类作为source创建了一个BeanDefinitionLoader,然后调用其load方法,也就是将主源类交给bean容器管理,这又是一种将对象交给bean容器管理的方法,也就是加载bean,加上上面的beanFactory.registerSingleton()表面上算是两种手动将对象交给bean容器的方法,可以学起来。这里展开就涉及到bean的加载了,关于bean的加载和这个BeanDefinitionLoader在第二篇提到过(https://blog.youkuaiyun.com/weixin_42447959/article/details/104943589)。

prepareContext()就看完了,下面是调用了refreshContext(),提前泄露刷新上下文这个步骤很重要,值得专门一篇博客,所以这篇就到这里。

总结一下:这篇遗留问题都解决了,主要介绍了run()方法中context三连的prepareContext()方法,里面就是各种给上下文context的set。学到了两种手动将对象交给bean容器管理的方法,还发现SpringBoot启动过程中事件发布机制真的很重要,经常一个方法做完一件事就发布一个对应的事件。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值