写点什么

spring4.1.8 扩展实战之五:改变 bean 的定义 (BeanFactoryPostProcessor 接口)

作者:程序员欣宸
  • 2022 年 6 月 16 日
  • 本文字数:6281 字

    阅读完需:约 21 分钟

spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本章我们继续实战 spring 的扩展能力,通过自定义 BeanFactoryPostProcessor 接口的实现类,来对 bean 实例做一些控制;

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());  }
复制代码


  • 在调用 PostProcessorRegistrationDelegate 类的 invokeBeanFactoryPostProcessors 方法时,注意第二个入参是 getBeanFactoryPostProcessors()方法,该方法返回的是 applicationContext 的成员变量 beanFactoryPostProcessors,该成员变量的值是哪里设置的呢?查找后发现,来自 AbstractApplicationContext.addBeanFactoryPostProcessor 方法被调用的时候:


@Overridepublic void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {  Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");  this.beanFactoryPostProcessors.add(postProcessor);}
复制代码


  • AbstractApplicationContext.addBeanFactoryPostProcessor 方法是留给业务扩展时调用的,例如在 springboot 初始化时,ConfigurationWarningsApplicationContextInitializer 类的 initialize 方法中就有调用:


@Overridepublic void initialize(ConfigurableApplicationContext context) {  context.addBeanFactoryPostProcessor(      new ConfigurationWarningsPostProcessor(getChecks()));}
复制代码


  • 看过了如何添加 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 方法;

  • 从上面的分析可以发现,所有实现了 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 下载源码,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章源码在文件夹 customizebeanfactorypostprocessor 下,如下图红框所示:



  • 接下来开始实战吧:

  • 基于 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>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>
复制代码


  • 定义一个服务接口 CalculateService:


package com.bolingcavalry.customizebeanfactorypostprocessor.service;
public interface CalculateService { /** * 整数加法 * @param a * @param b * @return */ int add(int a, int b);
/** * 返回当前实现类的描述信息 * @return */ String getServiceDesc();}
复制代码


  • 创建 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; }}
复制代码


  • 创建一个 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;
@Componentpublic 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";

  • 创建启动类 CustomizebeanfactorypostprocessorApplication:


package com.bolingcavalry.customizebeanfactorypostprocessor;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class CustomizebeanfactorypostprocessorApplication {
public static void main(String[] args) { SpringApplication.run(CustomizebeanfactorypostprocessorApplication.class, args); }}
复制代码


  • 启动应用,浏览器输入地址:http://localhost:8080/add/1/2,看到的响应如下图,红框中就是 CustomizeBeanFactoryPostProcessor 对名为 calculateService 的 bean 的定义对象修改后导致的结果:



  • 至此,BeanFactoryPostProcessor 的源码分析和扩展实战就结束了,通过本次实战,除了对 spring 扩展的认识加深,又掌握了一种控制 bean 的方式;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 刚刚阅读数: 5
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)_Java_程序员欣宸_InfoQ写作社区