本章我们继续实战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());
}
1
2
3

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);
}
1
2
3
4
5

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

@Override
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(
new ConfigurationWarningsPostProcessor(getChecks()));
}
1
2
3
4
5

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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
第七段,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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如上所示,两个方法都很简单,对每个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>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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

2. 定义一个服务接口CalculateService:

package com.bolingcavalry.customizebeanfactorypostprocessor.service;

public interface CalculateService {
/**
* 整数加法
* @param a
* @param b
* @return
*/
int add(int a, int b);

/**
* 返回当前实现类的描述信息
* @return
*/
String getServiceDesc();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上述代码的功能很简单,找到名为”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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12

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

至此,BeanFactoryPostProcessor的源码分析和扩展实战就结束了,通过本次实战,除了对spring扩展的认识加深,又掌握了一种控制bean的方式;
---------------------
作者:博陵精骑
来源:CSDN
原文:https://blog.csdn.net/boling_cavalry/article/details/82083889
版权声明:本文为博主原创文章,转载请附上博文链接!

spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)的更多相关文章

  1. spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)

    本章是<spring4.1.8扩展实战>系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean: 原文地址:https://blog.csdn.net/boli ...

  2. spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)

    本章是<spring4.1.8扩展实战>的第七篇,我们来尝试在容器初始化的时候对bean实例做设置: 原文地址:https://blog.csdn.net/boling_cavalry/a ...

  3. spring4.1.8扩展实战之八:Import注解

    spring4.1.8扩展实战之八:Import注解 2018年09月10日 12:53:57 博陵精骑 阅读数:441更多 所属专栏: spring4源码分析与实战    版权声明:欢迎转载,请注明 ...

  4. spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)

    本章是<spring4.1.8扩展实战>的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再 ...

  5. spring4.1.8扩展实战之三:广播与监听

    提到广播与监听,我们常常会想到RabbitMQ.Kafka等消息中间件,这些常用于分布式系统中多个应用之间,有时候应用自身内部也有广播和监听的需求(例如某个核心数据发生变化后,有些业务模块希望立即被感 ...

  6. spring4.1.8扩展实战之二:Aware接口揭秘

    Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就 ...

  7. [Java聊天室server]实战之五 读写循环(服务端)

    前言 学习不论什么一个稍有难度的技术,要对其有充分理性的分析,之后果断做出决定---->也就是人们常说的"多谋善断":本系列尽管涉及的是socket相关的知识,但学习之前,更 ...

  8. spring实战五之Bean的自动检测

    在spring实战四中,使用在Spring中增加<context:annotation-config>的方式告诉Spring,我们打算使用基于注解的自动装配,希望Spring特殊对待我们所 ...

  9. kubebuilder实战之五:operator编码

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

随机推荐

  1. STL hash function的模板特化

    哈希函数的作用是将一个值映射为一个哈希值,从而根据这个哈希值,在哈希表中对数据进行定位. template <class _Val, class _Key, class _HashFcn, cl ...

  2. Linux系统中tomcat的安装及优化

    Linux系统中Tomcat 8 安装 Tomcat 8 安装 官网:http://tomcat.apache.org/ Tomcat 8 官网下载:http://tomcat.apache.org/ ...

  3. java Map与Bean相互转化

    import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector ...

  4. [Python3] 018 if:我终于从分支中走出来了

    目录 0. 谁是主角 1. 从三大结构说起 (1) 顺序 (2) 分支 1) 分支的基本语法 2) 双向分支 3) 多路分支 (3) 循环 0. 谁是主角 分支是主角 我前面几篇随笔提到 if 不下2 ...

  5. Oracle数据库的发展历程

    前言 1970年的6月,IBM 公司的研究员埃德加·考特 (Edgar Frank Codd) 在 Communications of ACM 上发表了那篇著名的<大型共享数据库数据的关系模型& ...

  6. JUC并发包基本使用

    一.简介 传统的Java多线程开发中,wait.notify.synchronized等如果不注意使用的话,很容易引起死锁.脏读问题.Java1.5 版本开始增加 java.util.concurre ...

  7. sql server 应用bcp进行数据导出导入

    bcp 实用工具可以在 Microsoft SQL Server 实例和用户指定格式的数据文件间大容量复制数据. 使用 bcp 实用工具可以将大量新行导入 SQL Server 表,或将表数据导出到数 ...

  8. AngularJS语法

    1,$scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带. $scope是一个对象,有可用的方法和属性. $scope可应用在视图和控制器上.2,所有的 ...

  9. 安装webpack的流程及注意事项

    1)webpack依赖于node.js(node.js使用npm安装我们所依赖的js包) 2)安装npm(npm 全称是Node Package Manager,他是node包管理和分发工具) 3)通 ...

  10. WPF绑定各种数据源之xml数据源

    一.WPF绑定各种数据源索引 WPF 绑定各种数据源之Datatable WPF绑定各种数据源之object数据源 WPF绑定各种数据源之xml数据源 WPF绑定各种数据源之元素控件属性 Bindin ...