引言

在Spring中BeanFactoryPostProcessor(后面使用简写BFPP),作为容器启动过程的对容器进行修改操作的Bean对象,是Spring框架对外提供的核心扩展点之一,Spring IoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据),并可以修改它。同时可以定义多个

BeanFactoryPostProcessor源码

BeanFactoryPostProcessor源码如下

public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* 在标准初始化之后,修改应用程序上下文的内部Bean工厂。所有bean定义都将被加载,但尚未实例化任何bean。
* 这甚至可以覆盖或添加属性,甚至可以用于初始化bean。
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

通过源码中的注释说明,我们可以知道的是,BFPP是用于修改BeanFactory的,提供了一个回调方法,将BeanFactory作为参数,传给实现类,所以在获取到Bean工厂后,我们可以用它做IOC容器的任何操作。

BFPP有一个重要的子接口————BeanDefinitionRegistryPostProcessor(后文简写BDRPP)

BeanDefinitionRegistryPostProcessor(BDRPP)

BDRPP接口源码如下

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* 修改应用程序上下文的内部bean定义注册表。所有常规bean定义都将被加载,
* 但尚未实例化任何bean。这允许在下一个后处理阶段开始之前添加更多的bean定义。
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

这个接口提供了一个方法,用于将registry注册器传递给实现类,实现类可以用它向BeanDefinitionsMap中添加更多的Bean定义信息,这个地方只是添加Bean定义信息,并不是要向Bean容器注册一个实例Bean。比如ConfigurationClassPostProcessor的处理(Spring源码——ConfigurationClassPostProcessor类),用于发现更多的Bean,@Configuration注解可以将类里面@Bean注解标注了方法的Bean定义加载进来,这个我们会在另一篇中详细的讲解。

BeanFactoryPostProcessor,可以通过设置Ordered和其子接口PriorityOrdered来确定各个BeanFactoryPostProcessor执行顺序。

BFPP的执行时间

我们先来看一下Spring容器启动的核心方法,位于AbstractApplicationContext中的refresh()方法

	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 前戏,做容器刷新前的准备工作
* 1、设置容器的启动时间
* 2、设置活跃状态为true
* 3、设置关闭状态为false
* 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
* 5、准备监听器和事件的集合对象,默认为空的集合
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 创建容器对象:DefaultListableBeanFactory
// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// beanFactory的准备工作,对各种属性进行填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用各种beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
initMessageSource();
// Initialize event multicaster for this context.
// 初始化事件监听多路广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 留给子类来初始化其他的bean
onRefresh();
// Check for listener beans and register them.
// 在所有注册的bean中查找listener bean,注册到消息广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的单实例(非懒加载的)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// Reset 'active' flag.
// 重置active标志
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();
}
}
}

这是Spring容器在启动过程中,经历的13个启动方法,其中invokeBeanFactoryPostProcessors(beanFactory)

BFPP的具体调用执行

用于遍历执行Spring容器中的所有实现了BFPP接口的相关方法

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 获取到当前应用程序上下文的beanFactoryPostProcessors变量的值,并且实例化调用执行所有已经注册的beanFactoryPostProcessor
// 默认情况下,通过getBeanFactoryPostProcessors()来获取已经注册的BFPP,但是默认是空的,那么问题来了,如果你想扩展,怎么进行扩展工作?
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())方法中

里面内容很复杂,源码很长,所以就不粘上来了,大概思路如下

  1. 执行Spring框架添加的内部BDRPP,,它会通过入参集合的方式直接传递到具体执行方法中,比如ConfigurationClassPostProcessor(属于BDRPP)的postProcessBeanDefinitionRegistry方法
  2. 通过第1步的执行后,有可能会在容器BeanDefinitions中引入了一些新的自定义的BDRPP,还有一些原来解析XML文件时加入的BDRPP Bean,所以又执行自定义BDRPP的postProcessBeanDefinitionRegistry方法。(执行的时候 根据PriorityOrdered Ordered NoOrdered 的顺序依次执行)
  3. 通过上一步的执行后,还是有可能会引入新的BDRPP,根据PriorityOrdered Ordered NoOrdered 的顺序依次执行 postProcessBeanDefinitionRegistry方法。
  4. 循环第3步,直到不会产生新的BDRPP。
  5. 遍历执行容器中所有BFPP里的 postProcessBeanFactory方法,根据PriorityOrdered Ordered NoOrdered 的顺序依次执行

BFPP扩展

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//直接动态地向容器 一级缓存 中添加Bean实例
beanFactory.registerSingleton("xxxx", new Student("yanchuanbin"));
}
}

在获取到BeanFactory后,可以直接向一级缓存中加入Bean对象实例。

BDRPP扩展

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* BeanFactoryPostProcessor 的接口
*
* @param beanFactory the bean factory used by the application context
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//直接添加到一级缓存
beanFactory.registerSingleton("student", new Student("ycb"));
} /**
* BeanDefinitionRegistryPostProcessor 在BeanFactoryPostProcessor基础上 添加了一个接口
*
* @param registry the bean definition registry used by the application context
* @throws BeansException
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//添加Bean定义,但未将Bean实例化
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(Student.class)
.addPropertyValue("name", "yanchuanbin");
registry.registerBeanDefinition("xxxstudent", beanDefinitionBuilder.getBeanDefinition());
}
}

通registry向容器BeanDefinitionMap中添加Bean定义信息,跟上面的BFPP扩展有区别。

Spring扩展——BeanFactoryPostProcessor(BFPP)的更多相关文章

  1. spring扩展点整理

    本文转载自spring扩展点整理 背景 Spring的强大和灵活性不用再强调了.而灵活性就是通过一系列的扩展点来实现的,这些扩展点给应用程序提供了参与Spring容器创建的过程,好多定制化的东西都需要 ...

  2. Spring的BeanFactoryPostProcessor和BeanPostProcessor

    转载:http://blog.csdn.net/caihaijiang/article/details/35552859 BeanFactoryPostProcessor和BeanPostProces ...

  3. Spring点滴十一:Spring中BeanFactoryPostProcessor和BeanPostProcessor区别

    Spring中BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点.两个接口从名字看起来很相似,但是作用及使用场景却不同 ...

  4. spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  5. spring扩展点之二:spring中关于bean初始化、销毁等使用汇总,ApplicationContextAware将ApplicationContext注入

    <spring扩展点之二:spring中关于bean初始化.销毁等使用汇总,ApplicationContextAware将ApplicationContext注入> <spring ...

  6. spring扩展点之一:BeanFactoryPostProcessor和BeanPostProcessor

    一.BeanFactoryPostProcessor和BeanPostProcessor的区别 BeanFactoryPostProcessor和BeanPostProcessor都是spring初始 ...

  7. Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

    1.背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...

  8. Spring笔记(6) - Spring的BeanFactoryPostProcessor探究

    一.背景 在说BeanFactoryPostProcessor之前,先来说下BeanPostProcessor,在前文Spring笔记(2) - 生命周期/属性赋值/自动装配及部分源码解析中讲解了Be ...

  9. spring扩展点之五:ApplicationContextInitializer实现与使用

    ApplicationContextInitializer是Spring框架原有的东西,这个类的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的Appli ...

  10. Spring扩展之一:ApplicationContextInitializer

    1.介绍 用于Spring容器ConfigurableApplicationContext在刷新之前初始化Spring的回调接口. 通常在需要对应用程序上下文进行一些编程初始化的Web应用程序中使用. ...

随机推荐

  1. Linux系统诊断-内存基础

    简介: Linux系统诊断-内存基础 1. 背景 谈及linux内存,很多时候,我们会关注free,top等基础命令.当系统遇到异常情况时,内存问题的根因追溯,现场诊断时,缺乏深层次的debug能力. ...

  2. Spring Boot参数校验以及分组校验的使用

    简介: 做web开发基本上每个接口都要对参数进行校验,如果参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句.虽然这种方式简单直接,但会大大降低开发效率和代码可读性 ...

  3. IphoneX(10) 重启/关机, 强制重启/关机

    正常关机是同时长按 音量+ 和 右侧电源键,屏幕出现滑动按钮进行关机. 注意截图是同时短按 音量+ 和 右侧电源键. 强制关机是按照顺序按三个键:音量+   音量-  长按右侧键 Other:苹果X怎 ...

  4. [PHP] 浅谈 Laravel Scout 的存在意义

    注:Laravel Scout 是官方支持的对框架模型数据进行全文检索功能的扩展包. Laravel 的 Scout 与 Eloquent ORM 进行了深度集成,不用开发者再自己进行代码侵入了. L ...

  5. 数据改变后更新UI(SetProperty/RaiseCanExecuteChanged)

    View代码 1 <StackPanel> 2 <TextBlock Text="方法一"></TextBlock> 3 <TextBox ...

  6. 关于QQ群炸了的说明

    ABAP 7.5学习群不幸被腾讯封了,想要聊天的群友可以加以下两个群, ABAP 7.5历史研究小组 728466742 ABAP 7.5 备份群 582240105

  7. 🔥🔥httpsok-谷歌免费SSL证书如何申请

    httpsok-谷歌免费SSL证书如何申请 使用场景: 部署CDN证书.OSS云存储证书 证书类型: 单域名 多域名 通配符域名 混合域名 证书厂商: ZeroSSL Let's Encrypt Go ...

  8. iceoryx源码阅读(三)——共享内存通信(一)

    目录 0 导引 1 整体通信结构 2 RelativePointer 2.1 原理 2.2 PointerRepository 2.3 构造函数 2.4 get函数 3 ShmSafeUnmanage ...

  9. PageOffice 在线打开 word 文件实现痕迹保留、键盘批注、手写批注

    一.痕迹保留 Word中的痕迹一般指的是审阅文档的用户对文档所做的修改(插入和删除)操作.在PageOffice的强制留痕模式下,用户对文档所做的任何修改都会以痕迹的形式保留下来,不同用户对文档做的修 ...

  10. java rgb转hsv

    public static double[] toHSV(int r, int g, int b) { Color color = new Color(r, g, b); float[] hsv = ...