spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)
本章我们继续实战spring的扩展能力,通过自定义BeanFactoryPostProcessor接口的实现类,来对bean实例做一些控制;
原文地址:https://blog.csdn.net/boling_cavalry/article/details/82083889
BeanFactoryPostProcessor接口简介
spring容器初始化时,从资源中读取到bean的相关定义后,保存在beanFactory的成员变量中(参考DefaultListableBeanFactory类的成员变量beanDefinitionMap),在实例化bean的操作就是依据这些bean的定义来做的,而在实例化之前,spring允许我们通过自定义扩展来改变bean的定义,定义一旦变了,后面的实例也就变了,而beanFactory后置处理器,即BeanFactoryPostProcessor就是用来改变bean定义的;
源码分析
一起来看看上述功能对应的源码,从AbstractApplicationContext类的refresh方法看起,这里面对应着容器初始化的基本操作;
- 如下图所示,红框中的invokeBeanFactoryPostProcessors方法用来找出所有beanFactory后置处理器,并且调用这些处理器来改变bean的定义:
- 打开invokeBeanFactoryPostProcessors方法,如下所示,实际操作是委托PostProcessorRegistrationDelegate去完成的:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); }
3. 在调用PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法时,注意第二个入参是getBeanFactoryPostProcessors()方法,该方法返回的是applicationContext的成员变量beanFactoryPostProcessors,该成员变量的值是哪里设置的呢?查找后发现,来自AbstractApplicationContext.addBeanFactoryPostProcessor方法被调用的时候:
@Override public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) { Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null"); this.beanFactoryPostProcessors.add(postProcessor); }
4. AbstractApplicationContext.addBeanFactoryPostProcessor方法是留给业务扩展时调用的,例如在springboot初始化时,ConfigurationWarningsApplicationContextInitializer类的initialize方法中就有调用:
@Override public void initialize(ConfigurableApplicationContext context) { context.addBeanFactoryPostProcessor( new ConfigurationWarningsPostProcessor(getChecks())); }
5. 看过了如何添加BeanFactoryPostProcessor,再回到PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法,看看如何处理这些BeanFactoryPostProcessor,整个invokeBeanFactoryPostProcessors太大,不在此粘贴所有代码了,主要是分成七段来看分析这个方法:
第一段,入参中的BeanFactoryPostProcessor,按照是否实现了BeanDefinitionRegistryPostProcessor,分别放入两个集合:registryProcessors和regularPostProcessors;
第二段,找出所有实现了BeanDefinitionRegistryPostProcessor接口和PriorityOrdered接口的bean,放入registryProcessors集合,放入根据PriorityOrdered接口来排序,然后这些bean会被invokeBeanDefinitionRegistryPostProcessors方法执行;
第三段,找出所有实现了BeanDefinitionRegistryPostProcessor接口和Ordered接口的bean,放入registryProcessors集合,放入根据PriorityOrdered接口来排序,然后这些bean会被invokeBeanDefinitionRegistryPostProcessors方法执行;
第四段,对于那些实现了BeanDefinitionRegistryPostProcessor接口,但是没有实现PriorityOrdered和Ordered的bean也被找出来,然后这些bean会被invokeBeanDefinitionRegistryPostProcessors方法执行;
第五段,入参中的BeanFactoryPostProcessor,没有实现BeanDefinitionRegistryPostProcessor的那些bean,被invokeBeanDefinitionRegistryPostProcessors;
第六段,接下来的代码需要重点关注:找出实现了BeanFactoryPostProcessor接口的bean,注意这里已将面实现了BeanDefinitionRegistryPostProcessor接口的bean给剔除了,将这些bean分为三类:实现了PriorityOrdered接口的放入priorityOrderedPostProcessors,实现了Ordered接口的放入orderedPostProcessorNames,其他的放入nonOrderedPostProcessorNames,这段代码是关键,因为我们自定义的实现BeanFactoryPostProcessor接口的bean就会在此处被找出来,如下:
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }
第七段,priorityOrderedPostProcessors和orderedPostProcessorNames这两个集合,都是先做排序再调用invokeBeanDefinitionRegistryPostProcessors方法,最后是nonOrderedPostProcessorNames集合,也被传入invokeBeanDefinitionRegistryPostProcessors方法;
6. 从上面的分析可以发现,所有实现了BeanFactoryPostProcessor接口的bean,都被作为入参,然后调用了invokeBeanDefinitionRegistryPostProcessors或者invokeBeanFactoryPostProcessors方法去处理,来看看这两个方法:
private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } } /** * Invoke the given BeanFactoryPostProcessor beans. */ private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanFactory(beanFactory); } }
如上所示,两个方法都很简单,对每个BeanFactoryPostProcessor接口的实现类,都调用了其接口方法,不同的是,对于实现了BeanDefinitionRegistryPostProcessor接口的bean,调用其postProcessBeanDefinitionRegistry方法的时候,入参是BeanDefinitionRegistry,而非BeanFactory,因此,实现了BeanDefinitionRegistryPostProcessor接口的bean,其postProcessBeanDefinitionRegistry在被调用时,可以通过入参BeanDefinitionRegistry来做更多和bean的定义有关的操作,例如注册bean;
至此,对BeanFactoryPostProcessor的处理流程就全部分析完了,这里小结一下:
1. ApplicationContext扩展类可以调用AbstractApplicationContext.addBeanFactoryPostProcessor方法,将自定义的BeanFactoryPostProcessor实现类保存到ApplicationContext中;
2. spring容器初始化时,上一步中被加入到ApplicationContext的bean会被优先调用其postProcessBeanFactory方法;
3. 自定义的BeanFactoryPostProcessor接口实现类,也会被找出来,然后调用其postProcessBeanFactory方法;
4. postProcessBeanFactory方法被调用时,beanFactory会被作为参数传入,自定义类中可以使用该参数来处理bean的定义,达到业务需求;
5. 此时的spring容器还没有开始实例化bean,因此自定义的BeanFactoryPostProcessor实现类不要做与bean实例有关的操作,而是做一些与bean定义有关的操作,例如修改某些字段的值,这样后面实例化的bean的就会有相应的改变;
实战BeanFactoryPostProcessor接口的实现类
本次实战的内容是创建一个springboot工程,在里面自定义一个BeanFactoryPostProcessor接口的实现类,如果您不想敲代码,也可以去github下载源码,地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
这个git项目中有多个文件夹,本章源码在文件夹customizebeanfactorypostprocessor下,如下图红框所示:
接下来开始实战吧:
1. 基于maven创建一个springboot的web工程,名为customizebeanfactorypostprocessor,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bolingcavalry</groupId> <artifactId>customizebeanfactorypostprocessor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging> 20000 jar</packaging> <name>customizebeanfactorypostprocessor</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.15.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2. 定义一个服务接口CalculateService:
package com.bolingcavalry.customizebeanfactorypostprocessor.service; public interface CalculateService { /** * 整数加法 * @param a * @param b * @return */ int add(int a, int b); /** * 返回当前实现类的描述信息 * @return */ String getServiceDesc(); }
3. 创建CalculateService接口的实现类CalculateServiceImpl,注意要在Service注解上明确写入bean的名称:
package com.bolingcavalry.customizebeanfactorypostprocessor.service.impl; import com.bolingcavalry.customizebeanfactorypostprocessor.service.CalculateService; import org.springframework.stereotype.Service; @Service("calculateService") public class CalculateServiceImpl implements CalculateService { private String desc = "desc from class"; public void setDesc(String desc) { this.desc = desc; } @Override public int add(int a, int b) { return a + b; } @Override public String getServiceDesc() { return desc; } }
4. 创建一个BeanFactoryPostProcessor接口的实现类CustomizeBeanFactoryPostProcessor,并且用注解Component将其定义为spring环境中的bean:
package com.bolingcavalry.customizebeanfactorypostprocessor.beanfactorypostprocessor; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.stereotype.Component; @Component public class CustomizeBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("calculateService"); MutablePropertyValues pv = abstractBeanDefinition.getPropertyValues(); pv.addPropertyValue("desc", "Desc is changed from bean factory post processor"); abstractBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); } }
上述代码的功能很简单,找到名为”calculateService”的bean的定义对象,通过调用addPropertyValue方法,将定义中的desc属性值改为”Desc is changed from bean factory post processor”,这样等名为”calculateService”的bean被实例化出来后,其成员变量desc的值就是”Desc is changed from bean factory post processor”;
5. 创建启动类CustomizebeanfactorypostprocessorApplication:
package com.bolingcavalry.customizebeanfactorypostprocessor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class CustomizebeanfactorypostprocessorApplication { public static void main(String[] args) { SpringApplication.run(CustomizebeanfactorypostprocessorApplication.class, args); } }
6. 启动应用,浏览器输入地址:http://localhost:8080/add/1/2,看到的响应如下图,红框中就是CustomizeBeanFactoryPostProcessor对名为calculateService的bean的定义对象修改后导致的结果:
至此,BeanFactoryPostProcessor的源码分析和扩展实战就结束了,通过本次实战,除了对spring扩展的认识加深,又掌握了一种控制bean的方式;
《spring4.1.8扩展实战》系列文章链接:
1. 《spring4.1.8扩展实战之一:自定义环境变量验证》;
2. 《spring4.1.8扩展实战之二:Aware接口揭秘》;
3. 《spring4.1.8扩展实战之三:广播与监听》;
4. 《spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)》;
5. 《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》;
6. 《spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)》;
7. 《spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)》;
- spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)
- 关于Spring的Aware接口和后置处理器接口(BeanPostProcessor和BeanFactoryPostProcessor)的一些想法
- spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)
- Spring源码解析笔记9——容器的功能扩展BeanFactory的后处理(BeanFactoryPostProcessor)
- Spring 回顾 (Spring 扩展之 BeanPostProcessor 接口)
- Spring的BeanFactoryPostProcessor和BeanPostProcessor接口的区别
- Spring源码分析之BeanPostProcessor接口和BeanFactoryPostProcessor接口方法不执行原因分析
- 【Spring学习24】容器扩展点:后置处理器BeanFactoryPostProcessor
- 003-Spring4 扩展分析BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
- Spring源码分析之BeanPostProcessor接口和BeanFactoryPostProcessor接口方法不执行原因分析
- Spring 的 BeanPostProcessor接口实现
- Spring的BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor,ApplicationListener了解
- spring 容器后处理器 BeanFactoryPostProcessor 使用需要注意的一个问题
- Spring 的 BeanPostProcessor接口实现
- 流程图解Spring Framework(五) Spring BeanFactoryPostProcessor如何工作的?
- Spring 【 BeanPostProcessor】 处理器接口 与 【init-method 】配置属性
- Spring 的 BeanPostProcessor接口实现
- Spring Bean的生命周期之我的理解(一)------BeanFactoryPostProcessor和BeanPostProcessor
- Spring 的 BeanPostProcessor接口实现
- spring 后置处理器BeanFactoryPostProcessor和BeanPostProcessor的用法和区别