spring-boot 之深入理解 ApplicationContext

spring-boot 之深入理解 ApplicationContext

Spring核心:IoC容器

我们想要理解 Application Context ,首先得知道这个东西是用来解决什么的,我们知道Spring framework的核心是IoC容器,而Spring主要提供了两个容器系列,一个是实现BeanFactory接口的简单容器系列,其主要实现了容器最基本的功能;另一个就是 ApplicationContext 应用上下文,其在简单容器的基础上,增加了好多面向框架的特性,同时对应用环境进行了适配。

有了以上的认知后,我们来看IoC容器最基本的接口定义 BeanFactory ,BeanFactory 就是对于IoC容器从Client视角来看做的抽象,既然有了IoC容器,那容器管理的是什么呢?容器管理的是对象(Bean),我们需要对被管理的对象进行抽象,这就有了 BeanDefinition。

通过上面的讲述,我们已经有了几个基本的数据结构抽象了:

  • BeanFactory:对IoC容器的client端抽象

  • BeanDefinition:对IoC管理的对象的数据抽象

  • ApplicationContext:在 BeanFactory 基础上增加了面向框架的特性

我们再来看一个稍微复杂点的类图:

spring-boot 之深入理解 ApplicationContext

我们来简要说下上面的图,帮助理解。

  • 第一条线,从 BeanFactory 到 HierarchicalBeanFactory 再到 ConfigurableBeanFactory ,在这边主线上,BeanFactory 定义了IoC容器最基本的接口,如getBean,然后 HierarchicalBeanFactory 新增了了getParentBeanFactory接口,实现了在当前容器中找到不到Bean,可以递归查找父容器的功能,最后 ConfigurableBeanFactory 提供了配置BeanFactory的接口。

  • 第二条线,从 BeanFactory 到 ListableBeanFactory 再到 ApplicationContext ,这条线主要是在 ApplicationContext 接口中继承了 MessageSource, ApplicationEventPublisher, ResourcePatternResolver 等接口,在 BeanFactory 基础上新增了好多新功能。

BeanFactory

介绍完上面的类图后,我们下面具体来介绍 BeanFactory 和 ApplicationContext,先是 BeanFactory。

BeanFactory作为基础的IoC容器,管理了spring所有的Bean,提供了最基本的容器功能,但是BeanFactory是一个接口,现在默认的可使用实现是 DefaultListableBeanFactory, 我们来看下如何使用 DefaultListableBeanFactory 来实现Bean的注册:

spring-boot 之深入理解 ApplicationContext

此处配合 AnnotatedBeanDefinitionReader 完成了Bean的注册。

ApplicationContext

ApplicationContext 是一个高级形态的 IoC 容器,ApplicationContext 在 BeanFactory 基础上附加了好多其他功能:

spring-boot 之深入理解 ApplicationContext

  • MessageSource:支持不同的信息源,支持国际化实现

  • ResourcePatternResolver:访问资源,使得我们可以从不同的地方获取到Bean定义

  • ApplicationEventPublisher:提供事件机制,跟Bean的整个生命周期结合,方便扩展

以上是一些 ApplicationContext 提供的额外功能,正是这些功能使得 ApplicationContext 与 BeanFactory 相比,ApplicationContext 提供了更多面向框架的功能,因此我们一般使用 ApplicationContext 进行开发。

一个最简单的 ApplicationContext 的实现是 StaticApplicationContext,主要是为了测试而用,不应该在生产环境中使用,一个简单的使用示例:

spring-boot 之深入理解 ApplicationContext

其中重点是 refresh 方法,会涉及到IoC容器启动的一系列复杂操作,下面我们来具体分析下。

在具体介绍 refresh 之前,我们得明确 IoC 容器的最主要功能是Bean管理,那我们就必须要经历:

  1. 找到这些Bean定义

  2. 加载Bean定义

  3. 创建Bean

那我们就跟着上面的3步骤来看下refresh中是如何实现的。


StaticApplicationContext 自编码实现Bean定义、注册、创建

StaticApplicationContext 中不支持从外部配置文件中读取Bean,只支持编程的方式来注册Bean,我们来看下如何做:

spring-boot 之深入理解 ApplicationContext

spring-boot 之深入理解 ApplicationContext

上面的代码中我们定义了BeanContext来注册BeanDefinition,最后通过refresh来更新。

来看测试例子:

spring-boot 之深入理解 ApplicationContext

好了我们知道了简单的使用规则后,我们再来看看一个稍微复杂点的 ApplicationContext:AnnotationConfigApplicationContext,这个我们就可以完整的看到Bean3 部曲:寻找,载入,创建。我们先来看寻找。


BeanDefinition 的 Resource 定位

第一个寻找其实是Resource定位过程,spring将IO访问都抽象为Resource的访问,在 AnnotationConfigApplicationContext 中,将 Resource 定位委托给了 ClassPathBeanDefinitionScanner ,下面我们看下主要的类图:

spring-boot 之深入理解 ApplicationContext

其中

  • ResourcePatternResolver 用来从classpath中加载Resource;

  • MetadataReaderFactory 用来根据Resource生成MetadataReader;

  • BeanNameGenerator用来生成扫描到的Bean在容器中的名字;

  • ScopeMetadataResolver则用来处理扫描到的Bean的Scope。

我们先来看第一阶段Resource定位过程,此处Resource的定位是由ResourcePatternResolver来实现的,关于Resource相关的可以看文章Attack-spring-boot-3-1: 统一资源定位

在 AnnotationConfigApplicationContext 中,我们通过 scan 方法来寻找候选的Bean,跟踪代码我们会发现 scan 最终是调用到了 ResourcePatternResolver 来加载进 Resource, 关于资源定位,我们来看一个类图:

spring-boot 之深入理解 ApplicationContext

  • AbstractApplicationContext 继承了 DefaultResourceLoader,getResource(String)直接用DefaultResourceLoader的了

  • AbstractApplicationContext类的内 部声明有一个resourcePatternResolver,类型是 ResourcePatternResolver,对应的实例类型为 PathMatchingResourcePatternResolver

  • PathMatchingResourcePatternResolver构造的时候会接受一个ResourceLoader,而AbstractApplicationContext本身又继承自 DefaultResourceLoader,当然就直接把自身传递进去

  • ApplicationContext的实现类在作为ResourceLoader或者 ResourcePatternResolver时候的行为, 完全就是委派给了 PathMatchingResourcePatternResolver和DefaultResourceLoader来做。


BeanDefinition 注册

第二步骤是Bean的注册,也是在scan中完成:

spring-boot 之深入理解 ApplicationContext


Bean 创建

前面Bean的定位和注册都在scan中完成了,现在终于到了refresh函数了,我们来看下refresh中做了啥,根据前面的讲述,refresh中应该是开始对Bean进行创建了,实际上我们会发现 refresh 中还是会做对Bean的注册。

spring-boot 之深入理解 ApplicationContext

上面是整个refresh过程的概括,分别说下每步做什么。


prepareBeanFactory 标准初始化配置 BeanFactory

设置 BeanFactory 工厂创建 Bean 时使用什么样的类加载器,默认情况下使用线程上下文类加载器(默认为 AppClassLoader),添加 BeanPostProcessor,此处包含:ApplicationContextAwareProcessor 和 ApplicationListenerDetector。


postProcessBeanFactory 对 BeanFactory 进行个性化定制的扩展

这时候加载了 Bean 的定义,但是这时候还没有 Bean 被实例化,允许注册一些 BeanPostProcessors 类型的 Bean 用来在 Bean 初始化前后做一些事情。

例如 AbstractRefreshableWebApplicationContext 中就注册了 ServletContextAwareProcessor ,用来把 servletContext 设置到实现了 ServletContextAware 接口的 Bean。

spring-boot 之深入理解 ApplicationContext


invokeBeanFactoryPostProcessors 执行 BeanFactoryPostProcessor 来对 BeanFactory 进行扩展

通过委托给 PostProcessorRegistrationDelegate 来执行所有的 BeanFactoryPostProcessor。

此处先介绍两个接口 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor:

spring-boot 之深入理解 ApplicationContext

通过执行 BeanFactoryPostProcessor ,来对 BeanFactory 中的 Bean 定义进行修改,比如常见的是统一设置某些 Bean 的属性变量值, 对指定的 Bean 定义进行修改或者注册新的 Bean。

spring-boot 之深入理解 ApplicationContext

BeanDefinitionRegistryPostProcessor 接口继承自 BeanFactoryPostProcessor,它新添加了一个接口,用来在BeanFactoryPostProcessor 实现类中 postProcessBeanFactory 方法执行前再注册一些 Bean 到 beanFactory 中。

  • 在 invokeBeanFactoryPostProcessors 中首先执行实现了 BeanDefinitionRegistryPostProcessor 接口的 Bean 的 postProcessBeanDefinitionRegistry 方法

  • 然后再执行实现了 BeanFactoryPostProcessor 接口的 Bean 的 postProcessBeanFactory 方法

  • 由于接口的实现类可能会有多个,可以通过实现 PriorityOrdered 或者 Ordered 接口给每个Bean定义一个优先级,规定 PriorityOrdered 接口的优先级大于实现 Ordered 的优先级。

在 AnnotationConfigApplicationContext 中,会通过 AnnotatedBeanDefinitionReader 将相关的 BeanPostProcessor 预先注册到BeanFactory中,这其中就包括了 ConfigurationClassPostProcessor , 所以在此处 invokeBeanFactoryPostProcessors 中,就会调用 postProcessBeanDefinitionRegistry 方法来解析所有标注有 @Configuration 注解的类,并解析该类里面所有标注 @Bean 的方法和标注 @Import 的bean,并注入这些解析的 Bean 到 BeanFactory中。


registerBeanPostProcessors 注册 BeanPostProcessor 到 BeanFactory 的 beanPostProcessors 列表

BeanFactoryPostProcessor 是在 Bean 实例化前对 BeanFactory 进行扩展,BeanPostProcessor 是在 Bean 实例化后对 Bean 进行扩展,BeanPostProcessor 的接口定义如下:

spring-boot 之深入理解 ApplicationContext

在 registerBeanPostProcessors 中,主要是找寻实现了 BeanPostProcessor 的Bean,将其注册到BeanFactory 的 beanPostProcessors 属性里面,待后面使用。


onRefresh 为子类留下扩展,让子类能够继续注册特殊的Bean

在spring-boot 中 AnnotationConfigServletWebServerApplicationContext 的 onRefresh ,其代码如下:

spring-boot 之深入理解 ApplicationContext

在onRefresh中创建了内嵌的web服务器。


registerListeners 注册 ApplicationListeners

Spring 的 ApplicationContext 容 器 内 部 允 许 以 ApplicationEvent 的 形 式 发 布 事 件 , 容 器 内 注 册 的 ApplicationListener类型的bean定义会被ApplicationContext容器自动识别,它们负责监听容器内发布的所有 ApplicationEvent类型的事件,一旦容器内发布ApplicationEvent及其子类型的事件, 注册到容器的ApplicationListener就会对这些事件进行处理。

ApplicationEvent 的一个类图,是具体的事件类

spring-boot 之深入理解 ApplicationContext

ApplicationListener 是事件的监听器,在spring-boot启动的时候,会通过getSpringFactoriesInstances(ApplicationListener.class)加载所有的事件监听器到context中。

ApplicationContext 在具体实现事件的发布和事件监听器的注册功能委托给了 ApplicationEventMulticaster,整个类图如下:

spring-boot 之深入理解 ApplicationContext

AbstractApplicationEventMulticaster 是对 ApplicationEventMulticaster 的一个抽象类实现, SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster 的一个子类实现,添加了事件发布功能的实现。

ApplicationEventMulticaster 的初始化是在 initApplicationEventMulticaster 中完成。

在事件的发布上,默认是同步顺序发布,但是为了避免可能存在的性能问题 , 我们可以设置TaskExecutor来实现并发发布。

整个关系图:

spring-boot 之深入理解 ApplicationContext


finishBeanFactoryInitialization 创建所有非lazy-init的Bean

具体是调用 beanFactory.preInstantiateSingletons 来实现的

finishRefresh 应用程序上下文刷新结束,发送事件通知

通过 ApplicationEventMulticaster 发布 ContextRefreshedEvent事件

以上就是整个refresh的过程,refresh


总结

ApplicationContext 是Spring在 BeanFactory 基础容器之上,提供的另一个IoC容器实现。它拥 有许多 BeanFactory 所没有的特性,包括统一的资源加载策略、国际化信息支持、容器内事件发布。本文重点介绍了ApplicationContext的refresh过程,指出其主要工作就是做了Bean的注册、加载、初始化,下面一篇会继续介绍Bean实例化的具体过程,欢迎关注。

你的鼓励是我继续写下去的动力,期待我们共同进步,欢饮关注。


分享到:


相關文章: