写点什么

华为内部首次公开源码笔记,看完你还敢说你不会 SpringBoot?

发布于: 刚刚
华为内部首次公开源码笔记,看完你还敢说你不会SpringBoot?

今日分享开始啦,请大家多多指教~

大家使用 Spring Boot 这么久了,有没有知道 Spring Boot 的启动流程是怎样的呢?Spring 是如何扫描到使用 @Component 的类并且把它放进 BeanFactory 呢?它是如何启动的呢?现在我们就一起看看这个 Spring Boot 的启动过程。由于写这篇文章的时候,在很多环境写过,所以可能由于 Spring Boot 版本不同代码也不同,但是思路是一样的。

1.创建 SpringApplication 对象

1.main 方法

2.main 方法进来后先创建一个实例 new SpringApplication 蹩脚翻译一下。

创建一个新的 SpringApplication 实例也就是要 new SpringApplication(),并且定义主要的类作为 Bean 的加载来源。




2.在 SpringApplication 对象调用 run()

1.跑 run 方法


2.这个方法是从创建 SpringApplication 实例加载的应用类型去创建不同的上下文实例,这里由于是 SERVLET 类型所以会创建。

3.准备上下文,这里其实挺重要的,讲述了启动类如何加载到 BeanFactory,其实就一个重点,在 load()这个方法里面。


4.这里就先说下 doRegisterBean();

每个 bean 被执行 doGetBean 方法前都是要把 Bean 的定义信息拿到,也就是通俗的 BeanDefinition,类的元数据;

而 AnnotatedGenericBeanDefinition 就是为了公开这些类元数据做的接口;

当 adb 和 beanName 一起在 BeanDefinitionHolder 初始化的时候。


3.核心方法 refreshContext

1.此方法其实就是 Spring 整个启动过程或者说 ioc 等等的核心流程了,这个方法分几步去讲解。


2.这里进来就是 beanFactory 增强器加载。

3.这方法重中之重,里面包含了大量的操作我们来一起看看。

// Do not initialize FactoryBeans here: We need to leave all regular beans

// uninitialized to let the bean factory post-processors apply to them!

// Separate between BeanDefinitionRegistryPostProcessors that implement

// PriorityOrdered, Ordered, and the rest.

List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.

这里获取到的 beanName 不是刚刚的增强器而是常量

org.springframework.context.annotation.internalConfigurationAnnotationProcessor

并且下面这个循环会把 ConfigurationClassPostProcessor 加载出来。那么大家会问 ConfigurationClassPostProcessor 什么时候加载的呢?

createApplicationContext 这个方法还记得吧!就是在这里加载的当 Spring 选择了 Servlet 模式就会加载配置

CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME

就会作为 beanNamePut 进 beanFactory




First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.

Next, invoke the BeanFactoryPostProcessors that implement Ordered.

Finally, invoke all other BeanFactoryPostProcessors.

4.这里就是把启动类选出来并且开始 ComponentScan 的地方,自动装配也是这里完成的 @Import。

Parse each @Configuration class

这就是找 Component 的实现类

5.扫描 Component 和自动装配,这个也很重要,而且段代码有几个递归也是比较复杂,也是希望大家能够自己 debug 进去看看这到底是如何递归的。

Process any @ComponentScan annotations

这个为什么是数组呢?当你使用 @ComponentScans(value ={@ComponentScan("com.example.test"), @ComponentScan("com.example.test1")})


这里的长度就会变成 3

开始以启动类扫描,这就是为什么启动类永远在所有包的最外层,如果要扫描其他模块或者启动类以外的包就要 @ScanComponent 这个注解。

The config class is annotated with @ComponentScan -> perform the scan immediately

这里面就开始扫描 doScan 方法,只要是有 @Component 注解的都会把每个类的 Definition 放到 scannedBeanDefinitions 里面。

这里有事解析这个 Bean,跟刚刚的 parser.parse 一样,只是一个是数组一个是单个 Bean。

并且他会递归调用 doProcessConfigurationClass()方法,并且重新执行一次获取看看这个类有没有 @ComponentScan,并且继续扫描直至结束。

parse(bdCand.getBeanClassName(), holder.getBeanName());

Process any @Import annotations

其实每一个 parse 方法都会走到这里,把每个类的注解都会去循环一次,直至没有注解位置,会把 @Import 注解的类全部加载出来,这就是自动装配的原理。

这就是为什么我其他 jar 包的类可以给 Spring 管理 @Import 就是一个重点,把这个类导入到 BeanFactory 里面。

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

创建 Bean 实例

创建 bean 实例,这里的方法过于复杂用文章非常难解释,笔者这里就把大致的思路说下。

1.getBean 的时候先去 createBean 如果有就返回没有的话 doCreateBean。

2.当 doCreateBean 的时候就会触发 bean 的生命周期的各个接口。

3.其实笔者发现一个东西,ApplicationContextAware 并不算是 bean 生命周期的一环把,而是输入上下文的一环。

4.因为 ApplicationContextAware 其实是由添加了 AnnotationConfigServletWebServerApplicationContext 类所导致的。

5.创建 bean 的我会有下面的 uml 图让大家更能理解 Bean 是如何创建的。



关于 ApplicationContext

实现 ApplicationContextAware 接口会调用 setApplicationContext 方法,而 ApplicationContextAwareProcessor 又是实现 BeanPostProcessor,

而 ApplicationContextAwareProcessor 又被 Spring 强制注册,所以说如果一个 Bean 实现 ApplicationContextAware 和 BeanPostProcessor,

在先初始化有关于 BeanPostProcessor 的 Bean 时候会创建这个 Bean 创建这个 Bean 的时候又会调用 setApplicationContext 方法调用完之后最后才会调用 BeanPostProcessor 实现的方法,其实听拗口的,所以最后还是希望自己能 Debug 一下。

bean 生命周期流程图

小结

说到 Spring 其实很多都是 Spring 工程师,老搬砖奴,但是大家肯定也因为忙没时间去研究整个 Spring 的流程。

今日份分享已结束,源码资料,私信回复【1】......请大家多多包涵和指点!


用户头像

还未添加个人签名 2021.04.20 加入

Java工具与相关资料获取等WX: pfx950924(备注来源)

评论

发布
暂无评论
华为内部首次公开源码笔记,看完你还敢说你不会SpringBoot?