@zhuanxu
2018-03-07T17:11:05.000000Z
字数 7409
阅读 2501
spring-boot
我们想要理解 Application Context ,首先得知道这个东西是用来解决什么的,我们知道Spring framework的核心是IoC容器,而Spring主要提供了两个容器系列,一个是实现BeanFactory接口的简单容器系列,其主要实现了容器最基本的功能;另一个就是 ApplicationContext 应用上下文,其在简单容器的基础上,增加了好多面向框架的特性,同时对应用环境进行了适配。
有了以上的认知后,我们来看IoC容器最基本的接口定义 BeanFactory ,BeanFactory 就是对于IoC容器从Client视角来看做的抽象,既然有了IoC容器,那容器管理的是什么呢?容器管理的是对象(Bean),我们需要对被管理的对象进行抽象,这就有了 BeanDefinition。
通过上面的讲述,我们已经有了几个基本的数据结构抽象了:
我们再来看一个稍微复杂点的类图:
我们来简要说下上面的图,帮助理解。
介绍完上面的类图后,我们下面具体来介绍 BeanFactory 和 ApplicationContext,先是 BeanFactory。
BeanFactory作为基础的IoC容器,管理了spring所有的Bean,提供了最基本的容器功能,但是BeanFactory是一个接口,现在默认的可使用实现是 DefaultListableBeanFactory, 我们来看下如何使用 DefaultListableBeanFactory 来实现Bean的注册:
此处配合 AnnotatedBeanDefinitionReader 完成了Bean的注册。
ApplicationContext 是一个高级形态的 IoC 容器,ApplicationContext 在 BeanFactory 基础上附加了好多其他功能:
以上是一些 ApplicationContext 提供的额外功能,正是这些功能使得 ApplicationContext 与 BeanFactory 相比,ApplicationContext 提供了更多面向框架的功能,因此我们一般使用 ApplicationContext 进行开发。
一个最简单的 ApplicationContext 的实现是 StaticApplicationContext,主要是为了测试而用,不应该在生产环境中使用,一个简单的使用示例:
StaticApplicationContext simplestAppCtx = new StaticApplicationContext();
simplestAppCtx.refresh();
simplestAppCtx.close();
其中重点是 refresh 方法,会涉及到IoC容器启动的一系列复杂操作,下面我们来具体分析下。
在具体介绍 refresh 之前,我们得明确 IoC 容器的最主要功能是Bean管理,那我们就必须要经历:
那我们就跟着上面的3步骤来看下refresh中是如何实现的。
StaticApplicationContext 中不支持从外部配置文件中读取Bean,只支持编程的方式来注册Bean,我们来看下如何做:
上面的代码中我们定义了BeanContext来注册BeanDefinition,最后通过refresh来更新。
来看测试例子:
好了我们知道了简单的使用规则后,我们再来看看一个稍微复杂点的 ApplicationContext:AnnotationConfigApplicationContext,这个我们就可以完整的看到Bean3 部曲:寻找,载入,创建。我们先来看寻找。
第一个寻找其实是Resource定位过程,spring将IO访问都抽象为Resource的访问,在 AnnotationConfigApplicationContext 中,将 Resource 定位委托给了 ClassPathBeanDefinitionScanner ,下面我们看下主要的类图:
其中
我们先来看第一阶段Resource定位过程,此处Resource的定位是由ResourcePatternResolver来实现的,关于Resource相关的可以看文章Attack-spring-boot-3-1: 统一资源定位
在 AnnotationConfigApplicationContext 中,我们通过 scan 方法来寻找候选的Bean,跟踪代码我们会发现 scan 最终是调用到了 ResourcePatternResolver 来加载进 Resource, 关于资源定位,我们来看一个类图:
第二步骤是Bean的注册,也是在scan中完成:
前面Bean的定位和注册都在scan中完成了,现在终于到了refresh函数了,我们来看下refresh中做了啥,根据前面的讲述,refresh中应该是开始对Bean进行创建了,实际上我们会发现 refresh 中还是会做对Bean的注册。
上面是整个refresh过程的概括,分别说下每步做什么。
设置 BeanFactory 工厂创建 Bean 时使用什么样的类加载器,默认情况下使用线程上下文类加载器(默认为 AppClassLoader),添加 BeanPostProcessor,此处包含:ApplicationContextAwareProcessor 和 ApplicationListenerDetector。
这时候加载了 Bean 的定义,但是这时候还没有 Bean 被实例化,允许注册一些 BeanPostProcessors 类型的 Bean 用来在 Bean 初始化前后做一些事情。
例如 AbstractRefreshableWebApplicationContext 中就注册了 ServletContextAwareProcessor ,用来把 servletContext 设置到实现了 ServletContextAware 接口的 Bean。
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
}
通过委托给 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中。
BeanFactoryPostProcessor 是在 Bean 实例化前对 BeanFactory 进行扩展,BeanPostProcessor 是在 Bean 实例化后对 Bean 进行扩展,BeanPostProcessor 的接口定义如下:
public interface BeanPostProcessor {
//在Bean实例化后,初始化前进行一些扩展操作
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//在Bean实例化后,初始化后进行一些扩展操作
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
在 registerBeanPostProcessors 中,主要是找寻实现了 BeanPostProcessor 的Bean,将其注册到BeanFactory 的 beanPostProcessors 属性里面,待后面使用。
在spring-boot 中 AnnotationConfigServletWebServerApplicationContext 的 onRefresh ,其代码如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
在onRefresh中创建了内嵌的web服务器。
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来实现并发发布。
整个关系图:
具体是调用 beanFactory.preInstantiateSingletons 来实现的
通过 ApplicationEventMulticaster 发布 ContextRefreshedEvent事件
以上就是整个refresh的过程,refresh
ApplicationContext 是Spring在 BeanFactory 基础容器之上,提供的另一个IoC容器实现。它拥 有许多 BeanFactory 所没有的特性,包括统一的资源加载策略、国际化信息支持、容器内事件发布。本文重点介绍了ApplicationContext的refresh过程,指出其主要工作就是做了Bean的注册、加载、初始化,下面一篇会继续介绍Bean实例化的具体过程,欢迎关注。
你的鼓励是我继续写下去的动力,期待我们共同进步。
ConfigurationClassPostProcessor 读取Configuration配置类中的Bean