BeanFactoryPostProcessor 接口的英文描述: Allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory.

允许自定义修改应用程序上下文的 Bean Definitions,修改上下文的基础 bean工厂的 bean 属性值

分析 BeanFactoryPostProcessor ,此接口为 Spring IOC 的一个扩展点。 Spring容器启动过程中,获取 BeanFactory后,会调用该扩展点实现,主要作用是对已加载的 BeanDefinition 进行动态修改。

在解析完成 Bean 定义( XML 或者 JAVA 配置),封装成 BeanDefinition 对象,通过调用 BeanDefinitionRegistry.registry()的过程之后, 容器初始化 Bean 之前的这段时间之间,对已封装好的 Bean 定义 BeanDefinition 进行修改。

简要概括就是 Spring 加载 Bean 整体上分为 注册实例化 两步,在这两步中间执行的逻辑,其中之一就会对实现了 BeanFactoryPostProcessor接口的 Bean 进行操作,这也是 Spring 提供的一个扩展点。

BeanFactoryPostProcessor 扩展点时机

我们查看 AbstractApplicationContextrefresh() 方法

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 1 Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // 2 Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // 3 Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // 4 Initialize message source for this context.
initMessageSource(); // 5 Initialize event multicaster for this context.
initApplicationEventMulticaster(); // 6 Initialize other special beans in specific context subclasses.
onRefresh(); // 7 Check for listener beans and register them.
registerListeners(); // 8 Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // 9 Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

refresh() 首先通过 obtainFreshBeanFactory 获取到 BeanFactory 工厂,此处工厂实际为 DefaultListableBeanFactory,该工厂持有已解析的所有的Bean定义 BeanDefinition。refresh 在第二步执行 invokeBeanFactoryPostProcessors。这里主要是执行实现了 BeanFactoryPostProcessor 接口的 bean 对应的 接口方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate
.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ... 省略部分代码
}

PostProcessorRegistrationDelegate 及其子接口的扩展点机制

这里我们引入 BeanFactoryPostProcessor 的子接口BeanDefinitionRegistryPostProcessor。该接口的方法调用会比父接口更早,并且它新增了一个接口方法。定义如下:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
//在ApplicationContext 标准初始化之后修改其内部bean定义信息。
//此时所有常规bean定义都已加载,但还没有实例化bean。
// 这允许在下一个后期处理阶段开始之前进一步添加Bean定义
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

BeanDefinitionRegistryPostProcessor 的作用是,它提供支持注册新的BeanDefiniton (通过 postProcessBeanDefinitionRegistry 方法),并且在下一阶段仍可以被BeanFactoryPostProcessor去修改它的属性值。它在 BeanFactoryPostProcessor 之前执行。

BeanFactoryPostProcessor扩展点实现原理

PostProcessorRegistrationDelegate 核心代码

// ... 省略部分代码
// 执行没有实现 PriorityOrdered 和 Ordered 接口的普通的
// BeanDefinitionRegistryPostProcessor实现类
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
registryPostProcessors.add(pp);
processedBeans.add(ppName);
// 执行 postProcessBeanDefinitionRegistry
pp.postProcessBeanDefinitionRegistry(registry);
reiterate = true;
}
}
}
// BeanDefinitionRegistryPostProcessor 执行BeanFactoryPostProcessor接口的方法 postProcessBeanFactory
invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
// BeanFactoryPostProcessor执行起自身接口方法 postProcessBeanFactory
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  • 该执行过程在AbstractApplicationContext.refresh()方法中的第二步(try内代码)invokeBeanFactoryPostProcessors执行。
  • 找出BeanFactory中持有的实现了BeanDefinitionRegistryPostProcessor接口的bean定义。
  • 对实现类判断,判断是否实现了排序接口 PriorityOrdered 和 Ordered,如果有,优先执行他们
  • 如果没有找到,执行上文代码块里的逻辑,先call 接口定义的 postProcessBeanDefinitionRegistry,再执行从父接口 BeanFactoryPostProcessor 继承的方法 postProcessBeanFactory
  • BeanDefinitionRegistryPostProcessor执行完之后,最后回去执行实现了BeanFactoryPostProcessor 接口的 bean 的 postProcessBeanFactory 方法 。

BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor 用途

BeanFactoryPostProcessor 应用

典型应用:PropertySourcesPlaceholderConfigurer

我们非常常用的场景,在xml中配置数据源等信息时,会使用 SpringEL 表达式的形式去占位,然后真正的值会在配置文件中给出,配置如下:

 <!--该类的作用是加载配置文件,然后替换EL表达式-->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath*:application.properties"/>
</bean> <!--数据源配置-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</bean>

其实在 Spring 容器 registryBeanDefinition 时,即还没有初始化 Bean 之前,Bean 定义中的属性值这一块的值仍然是 EL 表达式。

容器在加载好 Bean 定义后,实例化 Bean 之前,会有一个钩子,这个钩子就是上文提到的 BeanFactoryPostProcessorPropertySourcesPlaceholderConfigurer 继承了这个接口,所以它的作用就是动态的替换所有已注册 Bean的BeanDefinition 信息中的 EL 的值为实际配置文件中的值。

BeanDefinitionRegistryPostProcessor 应用

典型应用:MapperScannerConfigurer

MapperScannerConfigurermybatis 整合 Spring 时使用的类。该类主要作用是提供对 mybatis Mapper 类的自动扫包操作,定义此 Bean 之后,通过配置basePackage属性,该类会去扫描相应的包下所有的接口类,然后将这些类的 Bean 定义改造为 MapperFactoryBean。可以有效的避免在 Spring 配置文件中为每一个Mapper注册对应的 MapperFactoryBean 类。

该类可以再 Spring 容器实例化 Bean之前,对已定义的 BeanDefinition 进行 修改。该类会通过 ClassPathMapperScanner 扫描器去扫描 指定 package 下面的接口类,然后对这个 BeanDefinition 进行修改,设置它的类型为 MapperFactoryBean

关于为什么 mybatis 要将 Mapper 接口类改造为 MapperFactoryBean ,将在后续文章中进行分析。

总结

BeanFactoryPostProcessor 接口是 Spring IOC 中 一个时机点比较早的扩展点钩子。它优先于 BeanPostProcessor 扩展点 ,我们需要区分这两个扩展点的不同之处。因为它们名字实在是太像了,BeanFactoryPostProcessor 接口的扩展点时机在 IOC 容器加载完成所有 BeanDefinition 后,实例化这些 Bean之前的点。

BeanPostProcessor 的时机点在容器准备开始实例化 Bean 时。BeanPostProcessor 扩展点又有很多的子接口,每一个子接口又处在不同的时机,所以后期分析 BeanPostProcessor 还任重而道远。

Spring-IOC 扩展点 BeanFactoryPostProcessor及其子接口解析的更多相关文章

  1. Spring IOC三种注入方式(接口注入、setter注入、构造器注入)(摘抄)

    IOC ,全称 (Inverse Of Control) ,中文意思为:控制反转, Spring 框架的核心基于控制反转原理. 什么是控制反转?控制反转是一种将组件依赖关系的创建和管理置于程序外部的技 ...

  2. Spring源码分析-BeanFactoryPostProcessor

    Spring源码分析-BeanFactoryPostProcessor 博主技术有限,本文难免有错误的地方,如果您发现了欢迎评论私信指出,谢谢 BeanFactoryPostProcessor接口是S ...

  3. Spring系列14:IoC容器的扩展点

    Spring系列14:IoC容器的扩展点 回顾 知识需要成体系地学习,本系列文章前后有关联,建议按照顺序阅读.上一篇我们详细介绍了Spring Bean的生命周期和丰富的扩展点,没有阅读的强烈建议先阅 ...

  4. Spring扩展点-v5.3.9

    Spring 扩展点 **本人博客网站 **IT小神 www.itxiaoshen.com 官网地址****:https://spring.io/projects/spring-framework T ...

  5. Mybatis是如何将Mapper接口注册到Spring IoC的

    1. 前言 有时候我们需要自行定义一些注解来标记某些特定功能的类并将它们注入Spring IoC容器.比较有代表性的就是Mybatis的Mapper接口.假如有一个新的需求让你也实现类似的功能你该如何 ...

  6. Spring Container的扩展点

    转自: http://blog.csdn.net/kkdelta/article/details/5488430 Spring在解析完配置文件后,会调用一些callback方法,使用Spring的开发 ...

  7. 【Spring IoC】Spring Bean(三)

    一.Spring Bean的定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. ...

  8. Spring IOC 概述

    Spring IOC 概述 IOC(Inversion of Control) 控制反转,也叫 DI(D_ependency injection_) 依赖注入.是一种设计思想.不过我并不同意所谓反转的 ...

  9. Spring IoC 容器的扩展

    前言 本篇文章主要介绍 Spring 中 BeanFactory 的扩展 ApplicationContext,我们平时日常开发中也基本上是使用它,不会去直接使用 BeanFactory. 那么在 S ...

随机推荐

  1. 中小型研发团队架构实践六:如何用好消息队列RabbitMQ?

    一.写在前面 使用过分布式中间件的人都知道,程序员使用起来并不复杂,常用的客户端 API 就那么几个,比我们日常编写程序时用到的 API 要少得多.但是分布式中间件在中小研发团队中使用得并不多,为什么 ...

  2. 一.JDK版本切换批处理脚本

    我们平时在window上做开发的时候,可能需要同时开发两个甚至多个项目,有时不同的项目对JDK的版本要求有区别,这时候我们可能会在一台电脑上安装多个版本的JDK,如下图所示:

  3. Access to XMLHttpRequest at 'XXX' from origin 'XX' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present o AJAX跨域请求解决方法

    今天出现了一个问题找了好久先看代码: 这可能是个BUG吧插入代码: dataType: 'jsonp', crossDomain: true, 最终:

  4. git 同步非master分支

    在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致: 建立本地分支和远程分支的关联,使用gi ...

  5. php面向对象高级-魔术方法与迭代器

    1,魔术方法__set与__get, __call >这些魔术方法,将在相关的属性或者方法不存在时调用 >函数原型 .function __set( $property, $value ) ...

  6. Code Signal_练习题_depositProfit

    You have deposited a specific amount of money into your bank account. Each year your balance increas ...

  7. Parcel + Vue 2.x 极速零配置打包体验

    继 Browserify.Webpack 之后,又一款打包工具 Parcel 横空出世 Parcel.js 的官网有这样的自我介绍 “极速零配置Web应用打包工具” 简单接触了一下,单从效率上来说,确 ...

  8. 【代码笔记】iOS-UIActionSheet动态添加按钮

    一,效果图. 二,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIVi ...

  9. FineReport中如何安装移动端H5插件

    1. HTML5报表插件安装及使用编辑 插件安装 插件网址以及设计器插件安装方法和服务器安装插件的方法可以官网上面搜索,这里就不做详细介绍了. 移动端HTML5报表使用方法 安装好插件后,在浏览器中调 ...

  10. 树莓派 温度监控 PWM 控制风扇 shell python c 语言

    Mine: 图中圈出来的是三极管 和滤波电容 依赖库: wiringPi sudo apt-get install wiringpi Shell脚本 本文介绍使用Shell脚本在树莓派上启用软件PWM ...