万字+20张图剖析Spring启动时核心的12个步骤

laomugua
发布于 2023-11-6 15:04
浏览
1收藏

大家好,我是三友~~

今天来扒一扒Spring在启动过程中核心的12个步骤

之所以来写这篇文章,主要是来填坑的

之前在​​三万字盘点Spring 9大核心基础功能​​这篇文章的末尾中给自己挖了一个坑,提了一嘴有机会要写这么一篇文章

但是由于Spring启动过程并不复杂,所以后面就没写了

不过,好巧不巧,刚刚好有兄弟来催更了,那么此时这个机会就来了,这篇文章也就有了

前言

Spring启动时候整个入口是这么一个方法

AbstractApplicationContext#refresh

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

总共有12个方法,也就是启动时的核心步骤

AbstractApplicationContext有众多实现,这里我选择SpringBoot Web应用默认的实现来讲

AnnotationConfigServletWebServerApplicationContext

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

对应的SpringBoot版本为 2.2.5.RELEASE

高版本refresh方法会多一些日志相关的代码,这里为了方便讲解,就使用这个版本

所以后面本文提到的所有子类的方法实现、重写都是指AnnotationConfigServletWebServerApplicationContext及其父类

本文主要是讲一些启动的步骤,具体的很多技术实现细节、技术点这里就不过多赘述

如果有疑问的,可以查看​​三万字盘点Spring 9大核心基础功能​​这篇文章或者公众号菜单栏中关于Spring的文章,基本上都能找到答案

prepareRefresh

prepareRefresh整个刷新的一个步骤,这个步骤是做启动的一个准备操作

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

ApplicationContext刚创建出来,什么也没有,所以需要做一些准备

首先是做一些状态位的变更,表明开始启动了或者刷新了

后面一行

initPropertySources

initPropertySources是一个模板方法,本身是一个空实现,是给子类用的

我们的这个子类就重写了initPropertySources方法

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

会将Servlet相关的配置加入到Environment中,这样我们就能从Environment中获取到Servlet相关的配置了

再后面一行

getEnvironment().validateRequiredProperties()

这行代码就是校验一些必要的配置属性,我们可以通过ConfigurableEnvironment来设置哪些属性是必要的,默认是没有必要的

所以prepareRefresh就是做了一些前置操作,准备好一些属性配置相关的东西,后面的其它环节,比如说生成Bean时可能需要用到这些配置

obtainFreshBeanFactory

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这一步骤是刷新BeanFactory并且获取BeanFactory

refreshBeanFactory() 和 getBeanFactory() 都是抽象方法,由子类来实现的

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

而子类的实现其实很简单,就是给beanFactory设置一个id和返回beanFactory

beanFactory就是下面这个玩意

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

并且创建对象的ApplicationContext对象的时候就创建了,类型为

DefaultListableBeanFactory

所以从这就可以看出来,虽然说BeanFactory是一个接口,有非常多的实现

但是实际情况下,真正使用的就是DefaultListableBeanFactory

并且DefaultListableBeanFactory其实算是BeanFactory唯一真正的实现

除此之外,还可以得出一个结论,ApplicationContext中有一个BeanFactory(DefaultListableBeanFactory)

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

prepareBeanFactory

上一步骤获取到了BeanFactory,但是这个BeanFactory仅仅就是刚刚new出来的,什么也没有

所以当前步骤就是对BeanFactory做一些配置工作

前三行代码

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

先给BeanFactory设置了一个ClassLoader,因为BeanFactory是用来创建Bean,需要加载Bean class对象

然后设置了一个BeanExpressionResolver,这个是用来解析SpEL表达式的

然后添加了一个PropertyEditorRegistrar,也就是

ResourceEditorRegistrar

这个的作用就是为BeanFactory添加一堆跟资源相关的PropertyEditor

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

PropertyEditor之前说过,就是进行类型转换的,将一个字符串转成对应的类型

接下来这几行

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这里主要是添加了一个BeanPostProcessor,也就是

ApplicationContextAwareProcessor

BeanPostProcessor我们都知道会在Bean的生命周期阶段进行回调,是Bean的生命周期一个核心的环节

ApplicationContextAwareProcessor这个是用来处理Bean生命周期中的Aware回调有关

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

当你的Bean实现这些接口的时候,在创建的时候Spring会回调这些接口,传入对应的对象

而后面的这行代码

beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

意思是说,如果你的Bean想注入一个EnvironmentAware对象

@Resource
private EnvironmentAware environmentAware;

这是不允许的

因为很简单,注入一个EnvironmentAware对象,没有实际的意义

后面的其它几行代码也都是这个意思

再接下来这几行

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这跟上面的ignoreDependencyInterface作用相反

他是来设置依赖注入时Bean的类型所对应的对象

比如说这行代码

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);

这行代码的意思就是当你需要注入一个Bean类型为BeanFactory.class类型的时候

@Resource
private BeanFactory beanFactory;

那么实际注入的就是方法第二个参数beanFactory,也就是上面获取的DefaultListableBeanFactory对象

同理,注入ResourceLoader、ApplicationEventPublisher、ApplicationContext时,其实注入的对象都是this,也就是当前的ApplicationContext对象

再再接下来这几行

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

最开始又添加了一个BeanPostProcessor

ApplicationListenerDetector

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这个BeanPostProcessor是跟ApplicationListener有关

他是将单例的ApplicationListener给添加到ApplicationContext中

再后面就是往BeanFactory里面添加一些跟配置属性相关的单例对象,如果有哪里用到,就可以从BeanFactory中获取到了

prepareBeanFactory就完了

正如方法名字的含义一样,就是对BeanFactory做一些配置相关的东西

比如添加一些BeanPostProcessor,注册一些PropertyEditor

为Bean的生成做准备操作

最后画张图来总结一下这个方法的作用

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

此时BeanFactory状态就是这样的

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

postProcessBeanFactory

这个方法是一个模板方法

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

本身是空实现,是交给子类来扩展,子类可以根据不同的特性再对BeanFactory进行一些准备工作

比如我们用的这个Web实现就重写了这个方法,对BeanFactory设置一些Web相关的配置

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

首先调用父类ServletWebServerApplicationContext的postProcessBeanFactory

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

前两行代码跟之前说的一样,也是添加Aware接口的回调对应的BeanPostProcessor,只不过这个Aware是跟Servlet相关的东西

接下来调用registerWebApplicationScopes方法,最终会调到下面这个方法

WebApplicationContextUtils#registerWebApplicationScopes

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这个方法干了两件事

第一件事就是注册一下Bean在Web环境下的作用域request、session,八股文中的东西

第二个就是注册一些依赖注入时Bean类型和对应的对象,这在日常开发中还是有用的

比如可以直接注入一个ServletRequest

@Resource
private ServletRequest servletRequest;

所以,父类的实现主要还是对BeanFactory进行一些配置,只不过配置的主要是跟Web环境相关的东西

现在来看看AnnotationConfigServletWebServerApplicationContext自身的实现

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

核心代码就是这两行

this.scanner.scan(this.basePackages);

this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));

scanner和reader就是下面这两个玩意

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

也就是说,如果这些配置都不是空的话,那么此时就会扫描对应的包的下Bean,生成对应的BeanDenifition,再注册到DefaultListableBeanFactory

至于为什么会存到DefaultListableBeanFactory中,可以看看之前的文章

此时BeanFactory大概是这么一个状态

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

除此之外,还有一个贼重要的事

AnnotationConfigServletWebServerApplicationContext这个ApplicationContext创建时会去创建AnnotatedBeanDefinitionReader

而AnnotatedBeanDefinitionReader的构造方法最终会调用这么一行代码

AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

这个方法非常重要,他会去注册一些BeanDefinition到BeanFactory中,这里我称为Spring内部的Bean

这里我说几个常见和重要的

  • ConfigurationClassPostProcessor:这个是用来处理配置类的,非常重要,记住这个类,后面有大用
  • AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value注解
  • CommonAnnotationBeanPostProcessor:处理@Resource、@PostConstruct等注解

所以除了扫描出来的一些Bean对应的BeanDefinition,还有一些Spring内部的Bean会注册到BeanFactory中

此时BeanFactory的状态就如下图所示

万字+20张图剖析Spring启动时核心的12个步骤-鸿蒙开发者社区

不过,在SpringBoot默认情况下,不会指定包和配置类,也就不会扫描文件,生成BeanDefinition

但是内部创建的BeanDefinition依然存在,并且在ApplicationContext创建的时候就注册到BeanFactory中了

所以总结来说,postProcessBeanFactory这个方法是交给子类对BeanFactory做一些准备操作,并且可能会扫描Bean


文章转载自公众号:三友的java日记

标签
已于2023-11-6 15:04:24修改
收藏 1
回复
举报
回复
    相关推荐