回顾
上文 其实我们已经实现了一个简单的 BeanFactory 它具的功能有
注册 Bean 到容器, 通过限定名获取 Bean
可以拦截 Bean 初始化前后的处理
可以在 Bean 属性注入后和即将销毁时做一些逻辑处理
解决了循环依赖
其实总结起来它实现的方法就是: 加载 Bean 定义, 实例化 Bean, 很简单吧
Bean 在 spring 中的完整生命周期, 可以自行查看 spring 的 BeanFactory 接口, 它在最上面的注释有详细说明.
但实际的应用场景除了这个主要 Bean 管理外, 还有一些消息广播, 国际化, 事件监听等
本文内容
本文想分析下 ApplicationContext 的 refresh 过程, 先来个总结性的过程, 以便后续一堆枯燥的源码分析
初始阶段: 准备工作
第一步, 我们先和得到一个 BeanFactory , 这里初始化的是 DefaultListableBeanFactory
第二步, 我们需要配置这个 BeanFactory 像 spel 表达式解析器, Environment 都要配置到工厂里面, 以便后续的处理
第三步, 我们允许添加 BeanFactoryProcessor 来修改 BeanFactory 中的 Bean , 常用的子类 BeanDefinitionRegistryPostProcessor 它可以往容器中添加 Bean 定义
像 @Import,@PropertySource,@ComponentScan,@ImportResource,@Bean methods 还有 tk.mybatis 都是靠它来注入自定义的 Bean 定义 的, 不清楚什么是 bean 定义看我的 上篇文章
第五步, 当然是 回调 上面收集到的所有的 BeanFactoryProcessor 了
第六步, 回调 所有收集到的 BeanPostProcessor, 这些 processor 是已经加到容器中的, 如果你看到这里, 不懂什么是 BeanPostProcessor 请回头看 上文 的 bean 的生命周期, 不清楚回调那就没办法了, 你还不够经验看这篇文章
第七步, 国际化初始化 (对于主体来说, 这算是支线, 因为大部分情况下我们都不是写的国际项目)
第八步, 初始化事件广播器. 事件: 常见的如页面加载完成有 onLoad 事件, 应用刚启动有 onLaunch 事件, 项目中用得不多, 可能是 spring 内部用得多吧
第九步, 留一个回调给子类来初始化特别的 bean , 默认空的
第十步, 注册监听器 (所有实现了 ApplicationListener 的类), 并把之前的一些事件发布一下
第十一步, 基本上是单例实例化, 基本上我们项目中的类都是在这个方法中实例化的
结束阶段: 清理工作
看这个过程的好处
熟悉 IoC 容器初始化的流程, 可以把 spring 用得更好
一些像 spel 工具类性质的东西可以直接拿过来用, 没必要引第三方库或重复造轮子
正文
不会把每一步都细品, 只会对一些关键的步骤进行解读, 像如何加载 Bean 定义, 读 xml 和读注解的, 读者可以自行研读, 本文讨论几个关键的 processor
我们都知道, Bean 的生命周期中, 可以添加 BeanPostProcessor 在 bean 的初始化前和初始化后做一些处理, 同样的在 BeanFactory 也有一个 BeanFactoryPostProcessor 允许你在 BeanFactory 初始化后, 修改 BeanFactory.
样例:
我们看一下 PropertyPlaceholderConfigurer xml 配置时代的产物, 它的继承结构如下
- PropertyResourceConfigurer implements BeanFactoryPostProcessor
- |-PlaceholderConfigurerSupport
- |-PropertyPlaceholderConfigurer
我们通常会在最开始配置这个
<context:property-placeholder location="classpath:jdbc.properties" />
PropertyPlaceholderConfigurer 用于对项目中的 @Value 值进行处理, 把值注入进属性中, 下面看下它如何实现的
查看关键方法 PropertyResourceConfigurer.postProcessBeanFactory, 分为三步
- Properties mergedProps = mergeProperties(); // 从 location 中加载属性
- // Convert the merged properties, if necessary.
- convertProperties(mergedProps); // 什么都没干, 一个模板方法, 可用于密码加密, 自定义属性转换
- // Let the subclass process the properties.
- processProperties(beanFactory, mergedProps); // 真正处理属性的地方, 解析 spel 表达式 ${
- }
真正的处理过程使用了访问者模式在 BeanDefinitionVisitor 中.
springboot 又是怎么处理的呢, springboot 是固定了配置文件 application.properties , 而不再使用 location 来配置路径, 它使用了 PropertySourcesPlaceholderConfigurer 来解析配置 , 它同样继承自 PlaceholderConfigurerSupport 并且重写了 postProcessBeanFactory 关键方法, 在最后面使用了
PropertySourcesPropertyResolver 进行了属性解析.
使用方式可以在 这篇文章 https://www.cnblogs.com/shamo89/p/8178109.html 看到
BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor 它的参数是 BeanDefinitionRegistry 一个 Bean 定义 的注册器, 它可以做些什么呢 , 查看它的方法, 可以对 bean 定义做增删改查, 厉害吧.
- registerBeanDefinition
- removeBeanDefinition
- getBeanDefinition
- containsBeanDefinition
- getBeanDefinitionNames
- getBeanDefinitionCount
- isBeanNameInUse
样例一:
在 ConfigurationClassPostProcessor 中, 它实现了 BeanDefinitionRegistryPostProcessor , 它使用 ConfigurationClassParser 来解析项目中配置的 @Configuration 类, 然后使用 ConfigurationClassBeanDefinitionReader 把解析到的类加载到 Bean 定义
具体细节查看 这篇文章
样例二:
在 tkmybatis 的 MapperScannerConfigurer 中, 也实现了 BeanDefinitionRegistryPostProcessor , 它使用 ClassPathMapperScanner 来扫描 Mapper 类, 添加进容器
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner
划重点
其实总结来说, refresh 的过程只是给你提供了一个大致的执行框架, 具体的处理还是在一些 processor,listener 中, 网上有大部分源码解读都拿这个 refresh 过程做大篇幅的讲解, 其实没多大必要, 就像 servlet 的生命周期一样, 了解其执行过程, 但剩下的处理都是子类去具现化的.
分享一篇说得不错的文章
BeanPostProcessor 和 BeanFactoryPostProcessor 浅析以及在 spring 初始化中回调
一点小推广
创作不易, 希望可以支持下我的开源软件, 及我的小工具, 欢迎来 gitee 点星, fork , 提 bug .
Excel 通用导入导出, 支持 Excel 公式
博客地址:
gitee: https://gitee.com/sanri/sanri-excel-poi
使用模板代码 , 从数据库生成代码 , 及一些项目中经常可以用到的小工具
博客地址:
gitee: https://gitee.com/sanri/sanri-tools-maven
来源: http://www.bubuko.com/infodetail-3281146.html