万字+20张图剖析Spring启动时核心的12个步骤
大家好,我是三友~~
今天来扒一扒Spring在启动过程中核心的12个步骤
之所以来写这篇文章,主要是来填坑的
之前在三万字盘点Spring 9大核心基础功能这篇文章的末尾中给自己挖了一个坑,提了一嘴有机会要写这么一篇文章
但是由于Spring启动过程并不复杂,所以后面就没写了
不过,好巧不巧,刚刚好有兄弟来催更了,那么此时这个机会就来了,这篇文章也就有了
前言
Spring启动时候整个入口是这么一个方法
AbstractApplicationContext#refresh
总共有12个方法,也就是启动时的核心步骤
AbstractApplicationContext有众多实现,这里我选择SpringBoot Web应用默认的实现来讲
AnnotationConfigServletWebServerApplicationContext
对应的SpringBoot版本为 2.2.5.RELEASE
高版本refresh方法会多一些日志相关的代码,这里为了方便讲解,就使用这个版本
所以后面本文提到的所有子类的方法实现、重写都是指AnnotationConfigServletWebServerApplicationContext及其父类
本文主要是讲一些启动的步骤,具体的很多技术实现细节、技术点这里就不过多赘述
如果有疑问的,可以查看三万字盘点Spring 9大核心基础功能这篇文章或者公众号菜单栏中关于Spring的文章,基本上都能找到答案
prepareRefresh
prepareRefresh整个刷新的一个步骤,这个步骤是做启动的一个准备操作
ApplicationContext刚创建出来,什么也没有,所以需要做一些准备
首先是做一些状态位的变更,表明开始启动了或者刷新了
后面一行
initPropertySources
initPropertySources是一个模板方法,本身是一个空实现,是给子类用的
我们的这个子类就重写了initPropertySources方法
会将Servlet相关的配置加入到Environment中,这样我们就能从Environment中获取到Servlet相关的配置了
再后面一行
getEnvironment().validateRequiredProperties()
这行代码就是校验一些必要的配置属性,我们可以通过ConfigurableEnvironment来设置哪些属性是必要的,默认是没有必要的
所以prepareRefresh就是做了一些前置操作,准备好一些属性配置相关的东西,后面的其它环节,比如说生成Bean时可能需要用到这些配置
obtainFreshBeanFactory
这一步骤是刷新BeanFactory并且获取BeanFactory
refreshBeanFactory() 和 getBeanFactory() 都是抽象方法,由子类来实现的
而子类的实现其实很简单,就是给beanFactory设置一个id和返回beanFactory
beanFactory就是下面这个玩意
并且创建对象的ApplicationContext对象的时候就创建了,类型为
DefaultListableBeanFactory
所以从这就可以看出来,虽然说BeanFactory是一个接口,有非常多的实现
但是实际情况下,真正使用的就是DefaultListableBeanFactory
并且DefaultListableBeanFactory其实算是BeanFactory唯一真正的实现
除此之外,还可以得出一个结论,ApplicationContext中有一个BeanFactory(DefaultListableBeanFactory)
prepareBeanFactory
上一步骤获取到了BeanFactory,但是这个BeanFactory仅仅就是刚刚new出来的,什么也没有
所以当前步骤就是对BeanFactory做一些配置工作
前三行代码
先给BeanFactory设置了一个ClassLoader,因为BeanFactory是用来创建Bean,需要加载Bean class对象
然后设置了一个BeanExpressionResolver,这个是用来解析SpEL表达式的
然后添加了一个PropertyEditorRegistrar,也就是
ResourceEditorRegistrar
这个的作用就是为BeanFactory添加一堆跟资源相关的PropertyEditor
PropertyEditor之前说过,就是进行类型转换的,将一个字符串转成对应的类型
接下来这几行
这里主要是添加了一个BeanPostProcessor,也就是
ApplicationContextAwareProcessor
BeanPostProcessor我们都知道会在Bean的生命周期阶段进行回调,是Bean的生命周期一个核心的环节
ApplicationContextAwareProcessor这个是用来处理Bean生命周期中的Aware回调有关
当你的Bean实现这些接口的时候,在创建的时候Spring会回调这些接口,传入对应的对象
而后面的这行代码
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
意思是说,如果你的Bean想注入一个EnvironmentAware对象
@Resource
private EnvironmentAware environmentAware;
这是不允许的
因为很简单,注入一个EnvironmentAware对象,没有实际的意义
后面的其它几行代码也都是这个意思
再接下来这几行
这跟上面的ignoreDependencyInterface作用相反
他是来设置依赖注入时Bean的类型所对应的对象
比如说这行代码
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
这行代码的意思就是当你需要注入一个Bean类型为BeanFactory.class类型的时候
@Resource
private BeanFactory beanFactory;
那么实际注入的就是方法第二个参数beanFactory,也就是上面获取的DefaultListableBeanFactory对象
同理,注入ResourceLoader、ApplicationEventPublisher、ApplicationContext时,其实注入的对象都是this,也就是当前的ApplicationContext对象
再再接下来这几行
最开始又添加了一个BeanPostProcessor
ApplicationListenerDetector
这个BeanPostProcessor是跟ApplicationListener有关
他是将单例的ApplicationListener给添加到ApplicationContext中
再后面就是往BeanFactory里面添加一些跟配置属性相关的单例对象,如果有哪里用到,就可以从BeanFactory中获取到了
prepareBeanFactory就完了
正如方法名字的含义一样,就是对BeanFactory做一些配置相关的东西
比如添加一些BeanPostProcessor,注册一些PropertyEditor
为Bean的生成做准备操作
最后画张图来总结一下这个方法的作用
此时BeanFactory状态就是这样的
postProcessBeanFactory
这个方法是一个模板方法
本身是空实现,是交给子类来扩展,子类可以根据不同的特性再对BeanFactory进行一些准备工作
比如我们用的这个Web实现就重写了这个方法,对BeanFactory设置一些Web相关的配置
首先调用父类ServletWebServerApplicationContext的postProcessBeanFactory
前两行代码跟之前说的一样,也是添加Aware接口的回调对应的BeanPostProcessor,只不过这个Aware是跟Servlet相关的东西
接下来调用registerWebApplicationScopes方法,最终会调到下面这个方法
WebApplicationContextUtils#registerWebApplicationScopes
这个方法干了两件事
第一件事就是注册一下Bean在Web环境下的作用域request、session,八股文中的东西
第二个就是注册一些依赖注入时Bean类型和对应的对象,这在日常开发中还是有用的
比如可以直接注入一个ServletRequest
@Resource
private ServletRequest servletRequest;
所以,父类的实现主要还是对BeanFactory进行一些配置,只不过配置的主要是跟Web环境相关的东西
现在来看看AnnotationConfigServletWebServerApplicationContext自身的实现
核心代码就是这两行
this.scanner.scan(this.basePackages);
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
scanner和reader就是下面这两个玩意
也就是说,如果这些配置都不是空的话,那么此时就会扫描对应的包的下Bean,生成对应的BeanDenifition,再注册到DefaultListableBeanFactory
至于为什么会存到DefaultListableBeanFactory中,可以看看之前的文章
此时BeanFactory大概是这么一个状态
除此之外,还有一个贼重要的事
AnnotationConfigServletWebServerApplicationContext这个ApplicationContext创建时会去创建AnnotatedBeanDefinitionReader
而AnnotatedBeanDefinitionReader的构造方法最终会调用这么一行代码
AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)
这个方法非常重要,他会去注册一些BeanDefinition到BeanFactory中,这里我称为Spring内部的Bean
这里我说几个常见和重要的
- ConfigurationClassPostProcessor:这个是用来处理配置类的,非常重要,记住这个类,后面有大用
- AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value注解
- CommonAnnotationBeanPostProcessor:处理@Resource、@PostConstruct等注解
所以除了扫描出来的一些Bean对应的BeanDefinition,还有一些Spring内部的Bean会注册到BeanFactory中
此时BeanFactory的状态就如下图所示
不过,在SpringBoot默认情况下,不会指定包和配置类,也就不会扫描文件,生成BeanDefinition
但是内部创建的BeanDefinition依然存在,并且在ApplicationContext创建的时候就注册到BeanFactory中了
所以总结来说,postProcessBeanFactory这个方法是交给子类对BeanFactory做一些准备操作,并且可能会扫描Bean
文章转载自公众号:三友的java日记