SpringBoot源码解读——启动过程以及自动装配过程

文章详细解析了SpringBoot的启动和自动装配过程,从SpringBoot的脚手架角色开始,逐步阐述启动类的run方法、初始化器和监听器的设置、加载配置、创建ApplicationContext上下文、环境准备、BeanDefinition的加载和解析,重点讲解了自动装配的核心部分,包括BeanDefinition的注册、配置类的解析、@Import注解的处理以及@EnableAutoConfiguration的实现,揭示了自动装配从获取配置到实例化Bean的复杂流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章会很长很复杂,如果大家想直接知道结果就直接看下面这个结论即可;

如何回答自动装配原理?

不要再按照一些帖子上说的什么@SpringBootApplication注解=》@EableAutoConfiguration=》@Import什么什么的了,这样回答只会让面试官觉得你根本没看过源码,只是去网上找了一篇帖子来背的;

因此我们在回答的时候要给面试官一种感受:我看过且了解过且理解了这个原理

 

 我们现在来逐步解析;

Spring和SpringBoot是什么关系?

SpringBoot其实就相当于一个脚手架,它是基于Spring为基石开发的;

SpringBoot项目的启动过程=》自动装配过程

我们在SpringBoot的启动类中会执行run方法,也就是启动该SpringBoot项目,我们来看看它在这个run方法里面做了什么;

准备工作:标记好对应的启动类解析启动类上的注解

1.首先创建了一个SpringApplication对象,传入当前启动类并并执行run方法(核心逻辑);

 2.在这个构造方法里面做了一些初始化工作:

  2.1 获取WebApplicationType(该知识点较为前沿不用深入理解):我们一般的web应用都是寄托于Servlet,因此大部分情况下这里都是直接返回的Servlet,而if是为了判断是否是响应式编程-WebFlux(后续再讲)的类型

  2.2 setInitializers,setListeners方法;设置初始化器和设置监听器,点开内部的getSpringFactoriesInstances方法

2.3 进入到loadFactoryNames方法中,首先获取到传过来的类的名字,然后调用loadSpringFactories方法;

  2.4 首先先到缓存里面去获取,如果获取不到则通过类加载器,传入我们指定的目录(META-INF/spring.factories)去获取;再对该资源进行解析,然后调用PropertiesLoaderUtils的loadPropertiies方法加载回来形成一个Properties文件;最后再进行遍历,把符合规则的数据加到缓存map中去

 

 

3.然后返回之后,在外面根据刚刚上面获取到的这些名称(类的全限定名), 进行实例化操作创建出来具体对象

 4.这个时候初始化器的设置就完成了,然后接下来就是**监听器的设置**,逻辑都是相同的,只不过传入的类的名称不同罢了;

 5.deduceMainApplicationClass方法:,在该方法里面通过异常拿到我们的对应栈信息,然后通过main函数获取具体的启动类,然后最终返回这个类即可;

6.在创建完SpringApplication方法之后正式进入它的run方法:

第一步创建了一个stopWatch并调用start方法(记录一下开始时间);

第二步创建一个ConfigurableApplicationContext上下文对象;

第三步创建了一个异常汇报器(不管);

第四步调用configureHeadlessProperty方法(配置Headless的属性,不管);

第五步获取监听器(上面初始化的时候已经设置了);

第六步,开始启动(该监听器涉及到观察者模式,不细讲,主要就是调用监听器开启事件);

第七部创建DefaultApplicationArguments对象(不管,是用来封装参数的);

第八步prepareEnvironment方法,封装环境对象,添加相关属性;

第九步printBanner=》控制台打印的那个图像;

第十步createApplicationContext方法创建上下文对象;

第十一步给异常报告器赋值

从下面这个地方开始就是核心十分重要的东西了

7.prepareContext方法

通过这个名字我们可以猜测一下,它的作用是准备上下文,我们进入这个方法

第一步:设置环境对象;

第二步:执行上下文的处理程序(postProcessApplicationContext);

第三步:应用初始化器

第四步:调用监听器的contextPrepared准备方法(又是观察者模式);

第五步:记录一下日志信息

第六步:获取一下当前的容器getBeanFactory;

第七步:注册一个SpringApplicationArguments对象(不管)

第八步:再注册一个SpringBootBanner对象;

第九步: 判断一下我们Bean定义信息是否允许被覆盖

第十步:一直走到最后的load方法

  它上面通过getAllSource方法去获取了一个sources的集合,这个primarySource就是我们初始化的时候设置的启动类,因此这个方法找的就是启动类=》目的是为了后续解析到启动类上的注解

 

进入到load方法

创建BeanDefinition的加载器,然后进行一系列的赋值;

 

调用BeanDefinition加载器的load方法:

遍历一下我们的sources,启动类我们只有一个,因此只需要循环一次,然后调用load重载方法

匹配这个启动类的类型(是Class对象)并加载;

然后判断完了之后再次调用load重载方法

我们直接看下面的isEligible方法

这个方法其实就是判断当前类是否能匹配到@Component注解

如果能匹配到,则返回true,执行内部的this.annotatedReader.register方法

这个annotatedReader其实就是一个注解读取器,用来读取目标类中的注解信息;

 

 我们把启动类放入register方法中,并不会完成注解的解析,它只是帮我们把启动类做一个最基本的标识(注册)

这个时候load方法就结束了,回到外面

第十一步:执行监听器的contextLoaded方法;来告诉我们已经加载完成了;

8.refreshContext方法(十分重要)

出现了这个方法,它底部一定会调用refresh方法(自动装配的入口)

而这一个方法里面又包含了13个方法:

prepareRefresh(准备刷新)

设置一些标志位,初始化一些最基本的容器;

obtainFreshBeanFactory

创建BeanFactory容器

prepareBeanFactory

为BeanFactory设置相关属性

postProcessBeanFactory

针对BeanFactory对象的后置处理器中的方法(前面已经讲过,这里就不再细说)

扩展机制,没有具体的实现,留给我们扩展的(模板方法模式)

invokeBeanFactoryPostProcessors

首先我们要明白,自动装配的实现就是为了从spring.factories文件中获取到对应的bean对象,并且由spring容器来帮我们管理,我们的Bean对象是要放在BeanFactory中,而我们知道SpringBoot是Spring的扩展实现,因此如果想要执行的话就得使用BeanFactory的后置处理器(BeanFactoryPostProcessor);

而该方法其实就是调用BeanFactory后置处理方法(自动装配核心)

 我们可以看到这个方法内部特别长但是比较简单,它就是使用了大量BeanDefinitionRegistryPostProcessor这个接口,该接口是BeanFactoryPostProcessor的一个子接口,看它的名字就知道它是针对BeanDefinition对象的一个后置处理器

首先它去BeanFacotory里面去找有没有归属于BeanDefinitionRegistryPostProcessor类型的对象,  

 但是我们执行完之后发现它获取到的是下面这个:

 但是我们直接搜是搜不到这个对象的,但是这个时候我们打开一个类叫AnnotationConfigUtils,在这个类里面找到了这个名称:

 然后在这个类下面我们可以看到下面这串代码,也就是说如果当我们的类中包含了上面名称的对象的时候,它会帮我们创建一个RootBeanDefinition对象,而这个对象里面放的是ConfigurationClassPostProcessor这个类,而这个类是BeanDefinitionRegistryPostProcessor的实现类

 也就是说我们去解析的类实际上是ConfigurationClassPostProcessor;

往下走,又执行了一个invokeBeanDefinitionRegistryPostProcessors方法来

 

 最终到这个方法内部去

首先循环遍历,最终找到我们的启动类生成BeanDefinitionHolder对象添加到候选者对象中

 解析贴了@Configuration注解  的类;

调用当前的解析器来进行解析候选对象(启动类):

 进行类型的判断(在prepareContext方法中,我们将启动类标识为了AnnotatedBeanDefinition);

 进入下面的parse

进入到这个processConfigurationClass方法中,扫描需要解析的类有哪些,然后再进入一个死循环进行解析,它会把带有继承关系的类全部解析下来,因此比较复杂:

首先它找到了我们的启动类,然后进入doProcessConfigurationClass方法中去:

首先判断启动类上面有没有@Component注解,如果有则进行相关解析;

再解析@PropertySource注解;

再解析@ComponentScan注解;

解析@Import注解;

我们不知道它怎么解析的,但是我们可以看到它这个方法后面有一个getImorts(sourceClass)方法,那么我们先进入这个方法去;

我们发现它创建了两个集合,然后进入到了collectIonImports方法中去

 在这个方法中,我们发现了这是一个递归操作,因为我们每一个注解下面的类又有其他的注解,例如我们的启动类@SpringBootApplication=》@EnableAutoConfiguration=》@Import,程序是不知道有多少层的,因此进行递归,拿到当前类的注解,遍历是否有@Import注解,如果没有则递归调用当前方法

 最后我们收集到了两个import注解,并且找到了它后面导入的两个类,分别是AutoConfigurationImportSelector和AutoConfigurationPackagesRegistrar;

 然后返回到外面,开始处理@ImportResource注解

在解析完成之后回到最外面,调用process方法

 创建了一个集合对象,然后循环调用了处理器中的processGroupImports方法

 先循环集合,然后通过getImports方法获取到import之后又进行了一次循环

进入到getImports方法,这个时候我们遍历这个集合里面就只有导入选择器一个类了,继续进入到process方法:

 

 这里面有一个很重要的方法:getAutoConfigurationEntry;我们点进去

这个方法里面我们发现了一个很熟悉的方法:getCandidateConfigurations;如果还不熟悉,我们再进去

这样看的话就熟悉了吧,它调用了loadFactoryNames方法,里面调用的getSpringFactoriesLoaderFactoryClass方法返回了一个EnableAutoConfiguration类

再进入这个方法里面,发现它已经有缓存了  

最终返回结果,然后在外面根据EnableAutoConfiguration类,取出对应的结果,也就是META-INFO/spring.factories中的所有的 EnableAutoConfiguration的值放进去,总共125个;

但是这些东西我们不是全部都用的到的,因此返回到外面去,检查一下我们要用到哪些,然后再把没有用到的remove掉,然后在remove掉之后,当前的Configuration中就只包含我们要用到的配置了(34个),并且其中还包含我们自定义的配置MyStarter;

 

到这一步就全部获取到了,后面的操作无非就是添加到对象中去并返回,然后再进行其他对象的实例化呗;

自动装配的过程就是以上这些,是非常复杂的,即使我们记住了,在面试的时候答完都差不多天昏地暗了,因此我们要思考如何简单易懂的回答出自动装配的原理

剩下的启动流程在后面会继续讲

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Strine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值