从根儿上学习spring 六 之run方法启动第三段

图1

图1--301行

这里主要是创建了spring的上下文context,大家可以点进这个createApplicationContext()方法看下,主要就是根据当前应用类型用反射创建一个context实例对象没啥逻辑我就不贴出来了。至于不同类型的context区别我们先不用纠结那么多细节,总之就是获取了一个applicationContext上下文对象,这个上下文是spring应用级别的,里面维护了spring核心的组件,比如上文聊到的environment对象,以及bean工厂beanFactory等。

图1--302至304行

这几行逻辑是获取异常上报处理类的,主要用在spring启动失败后catch里使用,不是我们关注的重点大家有兴趣时间宽裕自己看下吧。

图1--305行

执行了prepareContext方法,看名字就是用来准备好上下文对象的,废话不多说进去看看先。

图2

图2--362至363行

362行就不需要多说啥了,363行是对applicationContext就行后置处理,postProcessApplicationContext方法的逻辑是向applicationContext里设置BeanNameGenerator对象(该对象用来生成bean名称的,如果你有指定的话)以及设置ResourceLoader接口实现,该接口主要用来加载classpatch下的资源,后面用到我们再细说。

图2--364行

该行执行了applyInitializers(context)方法,看名字猜测是向applicationContext对象应用了多个Initializer,别猜了点进去看下吧。

图3

从图3我们看applyInitializers(context)方法的注释:在context被refresh前应用多个ApplicationContextInitializer到context上(英文翻译不知标不标准希望没误导)。

我们先看下ApplicationContextInitializer接口是个啥吧,不过从名字也能猜出来就是初始化applicationContext的。

图4

从图4我们看到ApplicationContextInitializer接口很简单,只有一个方法,该方法只有一个类型是ConfigurableApplicationContext的参数。方法执行时机大家也知道了就在applicationConext 被refresh方法执行前执行,其实就是上面图2的applyInitializers(context)方法执行的。比如你可以自定义一个实现类,在这个实现类里获取容器的环境变量来决定启用什么环境的配置文件。

我们再回到图2的prepareContext方法上来。

图2--365至376

这几行比较简单也不是重点大家自己看下吧。

图2--379行

调用getAllSources()方法获取所有资源并使用Set集合接收。获取的是什么资源呢,点开方法可知其实是将springApplication的两个属性添加到set集合,分别是Set> primarySources和Set sources。primarySources其实里面存的是我们启动时run方法里的启动类:springApplication.run(启动类)

而sources只能通过springApplication对象的set方法设值,我们一般很少有场景用到,所以通过getAllSources()获取到的一般只有启动类的class对象。至于为什么启动类对象可以作为资源以及这里获取到启动类之后的用处我们继续往下看。

图2--381行

该行调用load方法传入之前获取到的资源set集合以及context上下文,我们点进方法看看做了些啥骚操作。

图5

图5--681至695行

不关心BeanDefinitionLoader是个啥的话,直观的看这几行的逻辑就是创建了BeanDefinitionLoader对象并且对其设置了三个属性值,为了故事的继续我们需要对BeanDefinitionLoader做个简单了解。老规矩我们先看下这个类的定义

图6

从图6的BeanDefinitionLoader的注释可以了解到,它是用来加载bean的定义的,从哪加载呢?从xml配置或者javaConfig中,而且是作为XmlBeanDefinitionReader和AnnotatedBeanDefinitionReader一个简单的装饰。大白话再总结一般就是说BeanDefinitionLoader是用来从xml文件或者Java配置里加载spring的bean定义的,但是它其实只是作为XmlBeanDefinitionReader和AnnotatedBeanDefinitionReader的装饰器,具体的加载逻辑还是由这两个reader完成。那么为什么要加载bean定义呢,它又是个啥?spring的bean definitions对应到spring中的BeanDefinition接口,它是对需要被spring实例化为bean的class的完整描述,也就是说你写的类在被spring实例化对象前会先被spring维护成一个个的beanDefinition对象。然后spring再根据一个个的beanDefinition对象去创建bean对象。打个比喻,就像造车前需要先把车的一个个零部件准备好,而beanDefinition就存储了造成需要的一个个零部件,然后造成师傅根据beanDefinition里的一个个零部件把整车造出来。

解释完BeanDefinitionLoader我们直接到图5的696行的load()方法里看看它做了啥。

图7

可以看到图7的load方法只是循环调用132行的load(Object source)方法,而我们此刻的source是启动类的class对象所以会走到135行的load(Class)方法。

图8

如图8,我们不关心groovy逻辑直接看157行调用了isComponent(source)方法,该方法判断Class类有没有@component注解,我们的启动类都会有@SpringBootApplication注解该注解又被@SpringBootConfiguration注解,一层层最终会被@component注解,所以这个isComponent(source)方法会返回true执行下面的this.annotatedReader.register(source);方法,该方法的作用就是把我们的class资源封装成一个BeanDefinition交给spring,到这我们的启动类已经被解析成了一个BeanDefinition对象存放到spring容器中了。

总结

讲到这第三段的主要逻辑也就讲完了,这一段的主要任务就是准备好spring的上下文对象applicationContext对象,往里面设置了几个属性,哪几个属性大家往上翻看下,以及实例化了我们的启动类beanDefinition对象注册到了spring容器。接下来spring拿了我们启动类的beandefinition会做些什么事呢?我们下节继续分析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值