结束了前面的基础结构分析,了解到 Spring 是如何识别配置文件和进行解析属性,最终将 bean 加载到内存中。 同时为了更好得理解 Spring 的扩展功能,我们先来巩固一下 beanFactory 和 bean 的概念,然后再分析新内容后处理器 PostProcessor 。
本篇阅读思路:
Table of Contents generated with DocToc
首先我们先将 Spring 想像成一个大容器,然后保存了很多 bean 的信息,根据定义: bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象 ,也可以简单得理解为我们在配置文件配置好元数据, Spring IoC 容器会帮我们对 bean 进行管理,这些对象在使用的时候通过 Spring 取出就能使用。
Spring 管理呢,那就是 BeanFactory ,粗暴点直译为 bean 工厂,但其实它才是承担容器功能的幕后实现者,它是一个接口,提供了获取 bean 、获取别名 Alias 、判断单例、类型是否匹配、是否原型等方法定义,所以需要通过引用,实现具体方法才后才能使用。 回顾完 beanFactory 后,我们再来回顾在前面内容中,看到过很多后处理器 PostProcessor 的代码影子,分别是 BeanFactoryPostProcessor :主体是 BeanFactory , 和 BeanPostProcessor :主体是 Bean , 这两者都是 Spring 用来为使用者提供的扩展功能之一。
接下来为了更好的分析和了解使用后处理器,实现扩展功能,一起跟踪源码学习吧~
BeanFactoryPostProcessor 是一个接口,在里面只有一个方法定义:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
Spring 对外提供可扩展的接口,能够在容器加载了所有 bean 信息( AbstractApplicationContext#obtainFreshBeanFactory 方法)之后, bean 实例化之前执行,用来修改 bean 的定义属性。 ConfigurableListableBeanFactory beanFactory ,说明我们可以通过引用该接口,在方法中实现逻辑,对容器中 bean 的定义(配置元数据)进行处理 同时,执行后处理器是有先后顺序的概念,我们可以通过设置 order 属性来控制它们的执行次序,前提是 BeanFactoryPostProcessor 实现了 Order 接口。
下面一起来看下它的如何使用,以及是如何进行注册和执行~
这个类是 Spring 容器里自带的后处理器,是用来替换占位符,填充属性到 bean 中。
像我们在 xml 文件中配置了属性值为 ${max.threads} ,能够通过它来找到 max.threads 在配置文件对应的值,然后将属性填充到 bean 中。
PropertyPlaceholderConfigurer 已经打上了不建议使用的标志 @Deprecated ,看了文件注释,提示我们去使用 Environment 来设置属性,但我觉得这个后处理器的思想是一样的,所以还是拿它作为例子进行熟悉。 先来看下它的继承体系:
当 Spring 加载任何实现了 BeanFactoryPostProcessor 接口的 bean 配置时, 都会在 bean 工厂载入所有 bean 的配置之后执行 postProcessBeanFactory 方法 。
可以看到它引用了 BeanFactoryPostProcessor 接口,在 PropertyResourceConfigurer 父类中实现了 postProcessBeanFactory ,在方法中依次调用了合并资源 mergedProps 方法,属性转换 convertProperties 方法和 真正修改 beanFactory 中配置元数据的 processProperties(beanFactory, mergedProps) 方法 。
因为通过在 PropertyPlaceholderConfigurer 的后处理方法 postProcessBeanFactory , BeanFactory 在实例化任何 bean 之前获得配置信息, 从而能够正确解析 bean 描述文件中的变量引用 。
beanFactory 中的 bean 配置信息在实例化前还有机会进行修改。 题外话:想到之前我遇到全半角空格的配置问题,程序认为全半角空格不是同一个字符,但肉眼却很难察觉,所以感觉可以在加载配置信息时,通过自定义一个后处理,在实例化之前,将全角空格转成半角空格,这样程序比较时都变成统一样式。 所以后处理器提供的扩展功能可以让我们对想要处理的 bean 配置信息进行特定修改
实现的功能与书中的类似,例如之前西安奔驰汽车维权事件,如果相关网站想要屏蔽这奔驰这两个字,可以通过后处理器进行替换:
<bean id="carPostProcessor" class="context.CarBeanFactoryPostProcessor"> <property name="obscenties"> <!--set 属性--> <set> <value>奔驰</value> <value>特斯拉</value> </set> </property> </bean> <bean id="car" class="base.factory.bean.Car"> <property name="price" value="10000"/> <property name="brand" value="奔驰"/> </bean>
public class CarBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
/**
* 敏感词
*/
private Set<String> obscenties;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 从 beanFactory 中获取 bean 名字列表
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
StringValueResolver valueResolver = strVal -> {
if (isObscene(strVal)) return "*****";
return strVal;
};
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
// 这一步才是真正处理 bean 的配置信息
visitor.visitBeanDefinition(definition);
}
}
/**
* 判断 value 是否在敏感词列表中
* @param value 值
* @return boolean
*/
private boolean isObscene(Object value) {
String potentialObscenity = value.toString().toUpperCase();
return this.obscenties.contains(potentialObscenity);
}
}
public class BeanFactoryPostProcessorBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/factory-post-processor.xml");
// 这两行其实可以不写,因为在 refresh() 方法中,调用了一个函数将后处理器执行了,具体请往下看~
BeanFactoryPostProcessor beanFactoryPostProcessor = (BeanFactoryPostProcessor) context.getBean("carPostProcessor");
beanFactoryPostProcessor.postProcessBeanFactory(context.getBeanFactory());
// 输出 :Car{maxSpeed=0, brand='*****', price=10000.0},敏感词被替换了
System.out.println(context.getBean("car"));
}
}
通过上面的演示代码,新增一个自定义实现 BeanFactoryPostProcessor 的后处理器 CarBeanFactoryPostProcessor ,在 postProcessBeanFactory 方法中进行逻辑处理,最后通过 visitor.visitBeanDefinition 修改配置信息。
按照一般套路,后处理器需要有个地方进行注册,然后才能进行执行,通过代码分析,的确在 AbstractApplicationContext 中看到了 beanFactoryPostProcessors 数组列表,但往数组中添加后处理器的方法 addBeanFactoryPostProcessor 只在单元测试包调用了。
bean 信息加载时就放入注册表中,然后通过 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false) 方法获取后处理器列表遍历执行。 beanFactoryPostProcessors 数组列表,是让我们通过硬编码方法方式,手动添加进去,然后通过 context.refresh() 方法后,再执行硬编码的后处理器 例如下面这个例子
public class HardCodeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("Hard Code BeanFactory Post Processor execute time");
}
}
// 硬编码 后处理器执行时间
BeanFactoryPostProcessor hardCodeBeanFactoryPostProcessor = new HardCodeBeanFactoryPostProcessor();
context.addBeanFactoryPostProcessor(hardCodeBeanFactoryPostProcessor);
// 更新上下文
context.refresh();
// 输出:
//Hard Code BeanFactory Post Processor execute time
//Car{maxSpeed=0, brand='*****', price=10000.0}
System.out.println(context.getBean("car"));
看完了怎么使用后,我们来分析下 Spring 是如何识别和执行 BeanFactoryPostProcessor ,入口方法:
org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
实际上,委派了 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()) 代理进行执行。由于代码有点长,所以我画了一个流程图,可以结合流程图来分析代码:
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// beanFactory 默认使用的是 DefaultListableBeanFactory,属于 BeanDefinitionRegistry
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 两个后处理器列表
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 硬编码注册的后处理器
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 分类处理
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 首先,调用实现 priorityOrder 的 beanDefinition 这就是前面提到过的优先级概念,这一步跟下面的优先级不一样之处,这一步的优先级是带有权重
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 对后处理器进行排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 执行 definitionRegistryPostProcessor 接口的方法 :postProcessBeanDefinitionRegistry
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.最后,调用所有其他后处理器,直到不再出现其他 bean 为止
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// 执行 postProcessBeanFactory 回调方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 不要在这里初始化 factoryBean:我们需要保留所有常规 bean 未初始化,以便让 bean 工厂后处理程序应用于它们
// 注释 6.4 在这个步骤中,我们自定义的 carBeanFactoryPostProcessor 才真正注册并执行
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);.
// 跳过分类的逻辑
// 首先执行的是带有权重顺序的后处理器
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 下一步执行普通顺序的后处理器
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最后执行的是普通的后处理器
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除缓存的合并bean定义,因为后处理程序可能已经修改了原始元数据,例如替换值中的占位符
beanFactory.clearMetadataCache();
}
从上面贴的代码中能够看到,对于 beanFactoryPostProcessor 的处理主要分两种情况:
beanFactory 是 BeanDefinitionRegistry 类型 :需要特殊处理 beanFactory 不是 BeanDefinitionRegistry 类型 :进行普通处理 对于 BeanDefinitionRegistry 类型的处理器的处理主要包括以下内容:
List
registryPostProcessors :记录通过 硬编码 方式注册的 BeanDefinitionRegistryPostProcessor regularPostProcessors :记录通过 硬编码 方式注册的 BeanFactoryPostProcessor regitstryPostProcessorBeans :记录通过 配置 方式注册的 BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor 的 postProcessBeanFactory 方法 beanFactoryPostProcessors 中非 BeanDefinitionRegistryPostProcessor 类型的后处理器进行统一的 postProcessBeanFactory 方法 beanFactory 处理:其实在这一步中,就是忽略了 BeanDefinitionRegistryPostProcessor 类型,对 BeanFactoryPostProcessor 进行直接处理。 流程图中描述了整体调用链路,具体调用方法在代码中的注释也描述出来了,所以结合起来看应该能够理解整体流程~
本次分析了 beanFactory 的后处理器 BeanFactoryPostProcessor ,了解了 Spring 给我们提供的这个扩展接口使用用途和在源码中如何进行激活执行。
因为我也是一边看书,一边做笔记,下载了源码。 代码和注释都在里面,小伙伴们可以下载我上传的代码,亲测可运行~
spring-analysis-note 码云 Gitee 地址
spring-analysis-note Github 地址