Spring启动流程

4次阅读

共计 6302 个字符,预计需要花费 16 分钟才能阅读完成。

 Spring 用了挺久的了,但是没系统做过总结,刚好前段时间在做一个 Spring 封装的项目,趁机回顾了下,便基于 Spring framework 4.3.22 做了源码分析。

 刚开始接触 Spring 时的入门例子大致如下:

 设置配置文件路径,初始化 ApplicationContext 然后获取 Bean,处理完后关闭 context 即可。这一节先来了解 Spring 的启动过程。

一. 启动

 跟踪 ClassPathXmlApplicationContext 的构造方法可以看到如下内容:

 里面设置了配置文件的路径,并且调用了父类 AbstractApplicationContext 的 refresh 方法,该方法完成了 Spring 环境的初始化。如下,为 refresh 方法的主要过程(为方便排版,去除了原来的注释和空格):

 下面将介绍各个方法步骤的内容,但不进行过多的深入,后面会单独对每个深入的细节进行详细的介绍,这节先介绍大概过程。

1.1. try
1.prepareRefresh

 PrepareRefresh 的内容如上,该方法主要进行环境的准备,包括 Context 的启动时间,活动状态,然后会设置 context 中的配置数据源,使用默认的 StandardEnvironment 对象,该对象添加了 System.env() 属性和 System.properties() 属性。initPropertySources 方法用于初始化 context 中 environment 的属性源。在 AbstractApplicationContext 中为空实现。其他子类的实现如下:

 对于 GenericWebApplicationContext 和 AbstractRefreshableWebApplicationContext 的实现大致一致,都是:

 通过在 getEnvironment 方法中,重写 createEnvironment 方法,将默认的 StandardEnvironment 替换为 StandardServletEnvironment,Environment 的关系图为:

 因而会执行该类的 initPropertySources 方法,为 context 添加 ServletContext 和 ServletConfig 对应的配置属性源。具体的 Environment 中配置属性源的加载会在后面单独进行介绍。

2.obtainFreshBeanFactory

 该方法的实现如下,通过 refreshBeanFacotry 重置 AbstractApplicationContext 持有的 BeanFacotry,然后通过 getBeanFacotry 获得该对象再返回。

 AbstractApplicationContext 中 refreshBeanFacoty 方法和 getBeanFactory 方法都是抽象方法,具体实现在 AbstractRefreshableApplicationContext 上。

 如上,增加了方法的注释,重点在于 loadBeanDefinitions 方法,该抽象方法在具体实现子类上用于处理不同场景下 Bean 定义的加载,如 Xml 配置,注解配置,Web 环境等,具体实现会在后面展开。

 目前,只是完成了 Bean 定义的加载,没有出现 Bean 的实例化。

3.prepareBeanFactory

 为第 2 步返回的 BeanFactory 设置基础属性。包括:

  1. 设置 ClassLoader
  2. 设置 beanFactory 的表达式语言处理器,默认使用 EL 表达式,可以使用 #{bean.xxx} 的形式来调用相关属性值
  3. 添加默认的属性编辑器
  4. 添加后置处理器 ApplicationContextAwareProcessor,在 Bean 初始化后自动执行各 Aware 接口的 set 方法,包括 ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware
  5. 添加需要忽略的依赖注入类型,这些类型会在 ApplicationContextAwareProcessor 中通过 BeanPostProcessor 后置处理,包括第(4)点涉及的各内容
  6. 预先设置用于自动依赖注入的接口对象,包括 BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
  7. 如果存在 loadTimeWeaver 这个 Bean,则增加对应的后置处理器
  8. 如果不存在 environment,systemProperties,systemEnvironment 这 3 个默认的环境属性 Bean,则注册对应的单例,这 3 个对象已经在第 1 步中初始化完成

 具体可以看源码,这步主要预先设置公共的单例 Bean 并添加一些公共的后置处理动作,主要体现在 BeanPostProcessor 上。

4.postProcessBeanFactory

 所有 Bean 的定义已经加载完成,但是没有实例化,这一步可以修改 bean 定义或者增加自定义的 bean,AbstractApplicationContext 中为空实现。

 如上,以 AbstractRefreshableWebApplicationContext 为例,其增加了 ServletContextAwareProcessor 后置处理器,用于处理 ServletContextAware 接口和 ServletConfigAware 接口中相关对象的自动注入。同时新增了 Web 相关的应用范围,包括:request,session,globalSession 和 application,并增加了各范围默认的单例对象。最后增加了 Web 环境相关的环境配置 Bean, 包括 servletContext,servletConfig,contextParameters 和 contextAttributes。

 该步骤的功能同第 3 步类似,都能够增加一些后置处理器。

5.invokeBeanFactoryPostProcessors

 在 Spring 容器中找出实现了 BeanFactoryPostProcessor 接口的 Bean 并执行。Spring 容器会委托给 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法执行,内容如下:

 invokeBeanFactoryPostProcessors 在处理时,将 BeanFactoryPostProcessor 分为了两类进行处理,BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor,其中 BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor。执行的时候,先找出所有的 BeanDefinitionRegistryPostProcessor 执行再找出所有 BeanFactoryPostProcessor 执行。因为 BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,所以执行后者时会过滤掉前者的内容。

 在执行 BeanDefinitionRegistryPostProcessor 时,会按照如下的优先级,分类先执行 postProcessBeanDefinitionRegistry 方法,再统一执行所有的 postProcessBeanFactory 方法,,规则为:

  1. 筛选实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor 实现
  2. 筛选实现了 Ordered 接口的 BeanDefinitionRegistryPostProcessor 实现,并执行
  3. 执行其他 BeanDefinitionRegistryPostProcessors

 在执行 BeanFactoryPostProcessor 也会按照如上的规则,执行 BeanFactoryPostProcessor 方法。

这里会实例化并初始化实现 BeanFactoryPostProcessor 接口的类并执行,若存在依赖的的 Bean 也会被初始化和实例化,具体的过程会在介绍 Bean 初始化过程时说明。

6.registerBeanPostProcessors

 从 Spring 容器中找出的 BeanPostProcessor 接口的 Bean,并添加到 BeanFactory 内部维护的 List 属性中,以便后续 Bean 被实例化的时候调用这个 BeanPostProcessor 进行回调处理。该方法委托给了 PostProcessorRegistrationDelegate 类的 registerBeanPostProcessors 方法执行。执行过程同步骤 5 类似,也是按照优先级进行了筛选,具体顺序为:

  1. 将实现 PriorityOrdered 接口的 BeanPostProcessor 列表注册到 ApplicationContext 中
  2. 将实现 Ordered 接口的 BeanPostProcessor 列表注册到 ApplicationContext 中
  3. 将剩余的 BeanPostProcessor 列表注册到 ApplicationContext 中
  4. 将实现 MergedBeanDefinitionPostProcessor 接口的 BeanPostProcessor 列表注册到 ApplicationContext 中

 其中 MergedBeanDefinitionPostProcessor 接口继承自 BeanPostProcessor 接口,因而,上述第(4)点的列表同头三点的列表是存在交集的。但是,AbstraceApplicationContext 在添加 BeanPostProcessor 时,会先将存在的对象删除,再添加新的,如下:

 因而执行顺序为因而执行顺序为:PriorityOrdered、Ordered、NotOrdered、MergedBeanDefinitionPostProcessor。

这里会实例化并初始化实现 BeanPostProcessor 接口的类,但不执行,若存在依赖的的 Bean 也会被初始化和实例化。

7.initMessageSource

 在 Spring 容器中初始化一些国际化相关的属性

8.initApplicationEventMulticaster

 在 Spring 容器中初始化事件广播器对象 SimpleApplicationEventMulticaster,并将该对象作为单例 applicationEventMulticaster 注册到 Context 中。该广播器用于广播 ApplicationEvent 事件对应的 ApplicationListener 接口 Bean。

PS:根据以上的顺序,在这之前实例化的 Bean,都不会经过 BeanFactoryPostProcessor 和 BeanPostProcessor 的处理,包括因为依赖而实例化的 Bean, 还有提前通过 new 注册的 Bean(只有直接调用 BeanFactory.getBean 方法获取的 bean 才会进行后置回调)。这里需要注意,Context 提前将两种后置处理器的所有实现都提前加载了,由于实例化前需要将依赖的 Bean 提前实例化,所以被这两种后置处理器依赖的 Bean 的初始化动作,是不会被其监听到的。

9.onRefresh

 模板方法,可用于 refresh 动作的扩展,默认为空实现。在 SpringBoot 中主要用于启动内嵌的 web 服务器。

10.registerListeners

 找出系统中的 ApplicationListener 对象,注册到时间广播器中。如果有需要提前进行广播的时间,则执行广播.

11.finishBeanFactoryInitialization

 实例化 BeanFactory 中已经被注册但是未实例化的所有实例 (懒加载的不需要实例化),主要操作是 BeanFacotry 的 preInstantiateSingletons 方法。该方法分为两部分:

  1. 遍历已经解析出来的所有 beanDefinitionNames,如果不是抽象类、是单例且没有设置懒加载,则进行实例化和初始化。
  2. 在 spring 容器管理的所有单例对象(非懒加载对象)初始化完成之后调用 SmartInitializingSingleton 回调接口,注意,该回调只会发生在启动阶段,后续懒加载对象再初始化的话,不会再进行回调
  3. finishRefresh

 刷新后的其他动作,包括:

  1. 初始化生命周期处理器 DefaultLifecycleProcessor,该处理器管理所有实现了 Lifecycle 接口的类
  2. 通知所有 Lifecycle.onRefresh,该方法内部调用 LifecycleProcessor.startBeans(false),这里只会调用实现了 SmartLifecycle 接口,并且设定了 AutoStartup 的实例,回调将按照设定的优先级,从低到高执行
  3. 发布 ContextRefreshedEvent 通知事件
  4. 调用 LiveBeansView 的 registerApplicationContext 方法
1.2. catch
1.destroyBeans

 销毁所有已经注册的单例,对于实现了 DisposableBean 的类,会先单独进行销毁,以便执行回调方法,再清理所有单例的缓存信息和剩余的单例实例

2.cancelRefresh

 将当前的活动状态标识为 false

1.3. finally
  1. resetCommonCaches

 清除缓存

二. 关闭

 AbstractApplicationContext 的 close 方法如下:

 主要是调用 doClose 方法,然后判断是否有 shutdownHook,如果有则移除该钩子,避免重复关闭,因为默认的 shutdownHook 也是调用的 doClose 方法。

 doClose 方法如下:

 过程为:

  1. 去除当前 Context 的 MBean,如果开启了 MBean
  2. 发送 ContextClosedEvent 通知事件
  3. 回调声明周期管理器的 onClose 方法
  4. 销毁已经实例化的单例,同上面提到的一致
  5. 重置 BeanFacotry id 为空
  6. 调用 onClose 方法,默认实现为空,对于 SpringBoot 应用,在前面说过,SpringBoot 应用重写了 onRefresh 方法用于启动 web 服务器,而在这里,则用于关闭内嵌的 web 服务器。(PS:注意这里,web 服务器的关闭是在所有 Bean 销毁后再关闭的,因而在关闭服务器前,web 还会接收 Http 请求,有可能导致请求无法处理,官方给了一个解决方法,详见 issue https://github.com/spring-projects/spring-boot/issues/4657)

三. Start Stop 方法

 这两个方法来自 Lifecycle 接口,如下,简单的调用了 DefaultLifecycleProcessor 的 start 和 stop 方法,回调 Lifecycle 的实现类。

四. 扩展接口顺序

 我们知道 Spring 中存在很多预设的接口,用于扩展。通过以上分析,目前得到的回调接口顺序如下:

 后续对其他细节进行展开时,会看到更多的扩展接口,到时再更新上面的图。

个人公众号:啊驼

正文完
 0