[关闭]
@zhuanxu 2018-03-07T17:11:05.000000Z 字数 7409 阅读 2501

Attack-spring-boot-3:深入理解 Application Context

spring-boot


Spring核心:IoC容器

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

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

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

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

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

BeanFactory

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

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

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

ApplicationContext

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

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

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

  1. StaticApplicationContext simplestAppCtx = new StaticApplicationContext();
  2. simplestAppCtx.refresh();
  3. simplestAppCtx.close();

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

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

  1. 找到这些Bean定义
  2. 加载Bean定义
  3. 创建Bean

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

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

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



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

来看测试例子:

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

BeanDefinition 的 Resource 定位

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

其中

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

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

BeanDefinition 注册

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

Bean 创建

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


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


prepareBeanFactory 标准初始化配置 BeanFactory

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


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

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

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

  1. protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  2. beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
  3. }

invokeBeanFactoryPostProcessors 执行 BeanFactoryPostProcessor 来对 BeanFactory 进行扩展

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

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

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

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

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


registerBeanPostProcessors 注册 BeanPostProcessor 到 BeanFactory 的 beanPostProcessors 列表

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

  1. public interface BeanPostProcessor {
  2. //在Bean实例化后,初始化前进行一些扩展操作
  3. @Nullable
  4. default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  5. return bean;
  6. }
  7. //在Bean实例化后,初始化后进行一些扩展操作
  8. @Nullable
  9. default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  10. return bean;
  11. }
  12. }

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


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

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

  1. @Override
  2. protected void onRefresh() {
  3. super.onRefresh();
  4. try {
  5. createWebServer();
  6. }
  7. catch (Throwable ex) {
  8. throw new ApplicationContextException("Unable to start web server", ex);
  9. }
  10. }

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


registerListeners 注册 ApplicationListeners

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

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

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

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

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

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

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

整个关系图:


finishBeanFactoryInitialization 创建所有非lazy-init的Bean

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

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

通过 ApplicationEventMulticaster 发布 ContextRefreshedEvent事件


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

总结

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

你的鼓励是我继续写下去的动力,期待我们共同进步。
这个时代,每个人都是超级个体!关注我,一起成长!

ConfigurationClassPostProcessor 读取Configuration配置类中的Bean

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注