Spring扩展——BeanFactoryPostProcessor(BFPP)
引言
在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())方法中
里面内容很复杂,源码很长,所以就不粘上来了,大概思路如下
- 执行Spring框架添加的内部BDRPP,,它会通过入参集合的方式直接传递到具体执行方法中,比如ConfigurationClassPostProcessor(属于BDRPP)的postProcessBeanDefinitionRegistry方法
- 通过第1步的执行后,有可能会在容器BeanDefinitions中引入了一些新的自定义的BDRPP,还有一些原来解析XML文件时加入的BDRPP Bean,所以又执行自定义BDRPP的postProcessBeanDefinitionRegistry方法。(执行的时候 根据PriorityOrdered Ordered NoOrdered 的顺序依次执行)
- 通过上一步的执行后,还是有可能会引入新的BDRPP,根据PriorityOrdered Ordered NoOrdered 的顺序依次执行 postProcessBeanDefinitionRegistry方法。
- 循环第3步,直到不会产生新的BDRPP。
- 遍历执行容器中所有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)的更多相关文章
- spring扩展点整理
本文转载自spring扩展点整理 背景 Spring的强大和灵活性不用再强调了.而灵活性就是通过一系列的扩展点来实现的,这些扩展点给应用程序提供了参与Spring容器创建的过程,好多定制化的东西都需要 ...
- Spring的BeanFactoryPostProcessor和BeanPostProcessor
转载:http://blog.csdn.net/caihaijiang/article/details/35552859 BeanFactoryPostProcessor和BeanPostProces ...
- Spring点滴十一:Spring中BeanFactoryPostProcessor和BeanPostProcessor区别
Spring中BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点.两个接口从名字看起来很相似,但是作用及使用场景却不同 ...
- spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情
<spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...
- spring扩展点之二:spring中关于bean初始化、销毁等使用汇总,ApplicationContextAware将ApplicationContext注入
<spring扩展点之二:spring中关于bean初始化.销毁等使用汇总,ApplicationContextAware将ApplicationContext注入> <spring ...
- spring扩展点之一:BeanFactoryPostProcessor和BeanPostProcessor
一.BeanFactoryPostProcessor和BeanPostProcessor的区别 BeanFactoryPostProcessor和BeanPostProcessor都是spring初始 ...
- Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解
1.背景: 工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...
- Spring笔记(6) - Spring的BeanFactoryPostProcessor探究
一.背景 在说BeanFactoryPostProcessor之前,先来说下BeanPostProcessor,在前文Spring笔记(2) - 生命周期/属性赋值/自动装配及部分源码解析中讲解了Be ...
- spring扩展点之五:ApplicationContextInitializer实现与使用
ApplicationContextInitializer是Spring框架原有的东西,这个类的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的Appli ...
- Spring扩展之一:ApplicationContextInitializer
1.介绍 用于Spring容器ConfigurableApplicationContext在刷新之前初始化Spring的回调接口. 通常在需要对应用程序上下文进行一些编程初始化的Web应用程序中使用. ...
随机推荐
- Linux系统诊断-内存基础
简介: Linux系统诊断-内存基础 1. 背景 谈及linux内存,很多时候,我们会关注free,top等基础命令.当系统遇到异常情况时,内存问题的根因追溯,现场诊断时,缺乏深层次的debug能力. ...
- Spring Boot参数校验以及分组校验的使用
简介: 做web开发基本上每个接口都要对参数进行校验,如果参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句.虽然这种方式简单直接,但会大大降低开发效率和代码可读性 ...
- IphoneX(10) 重启/关机, 强制重启/关机
正常关机是同时长按 音量+ 和 右侧电源键,屏幕出现滑动按钮进行关机. 注意截图是同时短按 音量+ 和 右侧电源键. 强制关机是按照顺序按三个键:音量+ 音量- 长按右侧键 Other:苹果X怎 ...
- [PHP] 浅谈 Laravel Scout 的存在意义
注:Laravel Scout 是官方支持的对框架模型数据进行全文检索功能的扩展包. Laravel 的 Scout 与 Eloquent ORM 进行了深度集成,不用开发者再自己进行代码侵入了. L ...
- 数据改变后更新UI(SetProperty/RaiseCanExecuteChanged)
View代码 1 <StackPanel> 2 <TextBlock Text="方法一"></TextBlock> 3 <TextBox ...
- 关于QQ群炸了的说明
ABAP 7.5学习群不幸被腾讯封了,想要聊天的群友可以加以下两个群, ABAP 7.5历史研究小组 728466742 ABAP 7.5 备份群 582240105
- 🔥🔥httpsok-谷歌免费SSL证书如何申请
httpsok-谷歌免费SSL证书如何申请 使用场景: 部署CDN证书.OSS云存储证书 证书类型: 单域名 多域名 通配符域名 混合域名 证书厂商: ZeroSSL Let's Encrypt Go ...
- iceoryx源码阅读(三)——共享内存通信(一)
目录 0 导引 1 整体通信结构 2 RelativePointer 2.1 原理 2.2 PointerRepository 2.3 构造函数 2.4 get函数 3 ShmSafeUnmanage ...
- PageOffice 在线打开 word 文件实现痕迹保留、键盘批注、手写批注
一.痕迹保留 Word中的痕迹一般指的是审阅文档的用户对文档所做的修改(插入和删除)操作.在PageOffice的强制留痕模式下,用户对文档所做的任何修改都会以痕迹的形式保留下来,不同用户对文档做的修 ...
- java rgb转hsv
public static double[] toHSV(int r, int g, int b) { Color color = new Color(r, g, b); float[] hsv = ...