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 基础上增加了面向框架的特性
我们再来看一个稍微复杂点的类图:
我们来简要说下上面的图,帮助理解。
第一条线,从 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的注册:
此处配合 AnnotatedBeanDefinitionReader 完成了Bean的注册。
ApplicationContext
ApplicationContext 是一个高级形态的 IoC 容器,ApplicationContext 在 BeanFactory 基础上附加了好多其他功能:
MessageSource:支持不同的信息源,支持国际化实现
ResourcePatternResolver:访问资源,使得我们可以从不同的地方获取到Bean定义
ApplicationEventPublisher:提供事件机制,跟Bean的整个生命周期结合,方便扩展
以上是一些 ApplicationContext 提供的额外功能,正是这些功能使得 ApplicationContext 与 BeanFactory 相比,ApplicationContext 提供了更多面向框架的功能,因此我们一般使用 ApplicationContext 进行开发。
一个最简单的 ApplicationContext 的实现是 StaticApplicationContext,主要是为了测试而用,不应该在生产环境中使用,一个简单的使用示例:
其中重点是 refresh 方法,会涉及到IoC容器启动的一系列复杂操作,下面我们来具体分析下。
在具体介绍 refresh 之前,我们得明确 IoC 容器的最主要功能是Bean管理,那我们就必须要经历:
找到这些Bean定义
加载Bean定义
创建Bean
那我们就跟着上面的3步骤来看下refresh中是如何实现的。
StaticApplicationContext 自编码实现Bean定义、注册、创建
StaticApplicationContext 中不支持从外部配置文件中读取Bean,只支持编程的方式来注册Bean,我们来看下如何做:
上面的代码中我们定义了BeanContext来注册BeanDefinition,最后通过refresh来更新。
来看测试例子:
好了我们知道了简单的使用规则后,我们再来看看一个稍微复杂点的 ApplicationContext:AnnotationConfigApplicationContext,这个我们就可以完整的看到Bean3 部曲:寻找,载入,创建。我们先来看寻找。
BeanDefinition 的 Resource 定位
第一个寻找其实是Resource定位过程,spring将IO访问都抽象为Resource的访问,在 AnnotationConfigApplicationContext 中,将 Resource 定位委托给了 ClassPathBeanDefinitionScanner ,下面我们看下主要的类图:
其中
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, 关于资源定位,我们来看一个类图:
AbstractApplicationContext 继承了 DefaultResourceLoader,getResource(String)直接用DefaultResourceLoader的了
AbstractApplicationContext类的内 部声明有一个resourcePatternResolver,类型是 ResourcePatternResolver,对应的实例类型为 PathMatchingResourcePatternResolver
PathMatchingResourcePatternResolver构造的时候会接受一个ResourceLoader,而AbstractApplicationContext本身又继承自 DefaultResourceLoader,当然就直接把自身传递进去
ApplicationContext的实现类在作为ResourceLoader或者 ResourcePatternResolver时候的行为, 完全就是委派给了 PathMatchingResourcePatternResolver和DefaultResourceLoader来做。
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。
invokeBeanFactoryPostProcessors 执行 BeanFactoryPostProcessor 来对 BeanFactory 进行扩展
通过委托给 PostProcessorRegistrationDelegate 来执行所有的 BeanFactoryPostProcessor。
此处先介绍两个接口 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor:
通过执行 BeanFactoryPostProcessor ,来对 BeanFactory 中的 Bean 定义进行修改,比如常见的是统一设置某些 Bean 的属性变量值, 对指定的 Bean 定义进行修改或者注册新的 Bean。
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 的接口定义如下:
在 registerBeanPostProcessors 中,主要是找寻实现了 BeanPostProcessor 的Bean,将其注册到BeanFactory 的 beanPostProcessors 属性里面,待后面使用。
onRefresh 为子类留下扩展,让子类能够继续注册特殊的Bean
在spring-boot 中 AnnotationConfigServletWebServerApplicationContext 的 onRefresh ,其代码如下:
在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实例化的具体过程,欢迎关注。
你的鼓励是我继续写下去的动力,期待我们共同进步,欢饮关注。
閱讀更多 程序猿的進擊之路 的文章