同步操作将从 xuchengsheng/spring-reading 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
✒️ 作者 - Lex 📝 博客 - 我的CSDN 📚 文章目录 - 所有文章 🔗 源码地址 - BeanFactoryPostProcessor源码
BeanFactoryPostProcessor
是一个接口,任何实现此接口的类都必须提供postProcessBeanFactory
方法的实现。此方法提供了一个机会,在bean实例化之前修改bean的定义。
BeanFactoryPostProcessor
是 Spring 框架自 06.07.2003 开始引入的一个核心接口。此接口提供了一个强大的方式来修改bean的属性和依赖关系,使得我们可以根据特定的配置或环境条件动态地调整这些值。
/**
* BeanFactoryPostProcessor 是一个核心接口,允许用户在Spring容器初始化bean之前修改bean定义。
* 它提供了一个强大的方式来修改bean的属性和依赖关系,使得我们可以根据特定的配置或环境条件动态地调整这些值。
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* 在应用上下文的内部bean工厂进行其标准初始化后修改它。
* 此时,所有bean定义都已加载,但尚未实例化任何bean。
* 这允许用户即使对于急切初始化的beans也可以覆盖或添加属性。
*
* @param beanFactory 应用上下文使用的bean工厂
* @throws org.springframework.beans.BeansException 如果发生错误
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
修改Bean定义
BeanFactoryPostProcessor
会被调用。这意味着我们可以使用它来修改这些bean定义。更改属性值
添加或删除Bean定义
应用多个BeanFactoryPostProcessors
BeanFactoryPostProcessor
,我们可以通过实现Ordered
接口来控制它们的执行顺序。首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext
(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration
组件类。然后我们会调用mySimpleBean1
和mySimpleBean2
中的show()
方法,我们可以判断MySimpleBean
的作用域是单例还是原型。如果它们指向同一个实例,那么它是单例的;否则,它是原型的。
public class BeanFactoryPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class);
MySimpleBean mySimpleBean2 = context.getBean(MySimpleBean.class);
mySimpleBean1.show();
mySimpleBean2.show();
}
}
这里使用@Bean
注解,定义了两个Bean,是为了确保MySimpleBean
, MyBeanFactoryPostProcessor
被 Spring 容器执行
@Configuration
public class MyConfiguration {
@Bean
public MySimpleBean mySimpleBean(){
return new MySimpleBean();
}
@Bean
public static MyBeanFactoryPostProcessor myBeanFactoryPostProcessor(){
return new MyBeanFactoryPostProcessor();
}
}
MySimpleBean
类中的show
方法在被调用时,会在控制台输出“MySimpleBean instance”和当前对象的实例地址(通过this
关键字)。这有助于我们了解每次获取bean时是否返回相同的实例(单例)还是新的实例(原型)。
public class MySimpleBean {
public void show() {
System.out.println("MySimpleBean instance: " + this);
}
}
这个 MyBeanFactoryPostProcessor
类是一个简单的 BeanFactoryPostProcessor
的实现,它在被调用时,会从beanFactory
工厂中获取名为mySimpleBean
的bean定义,默认情况下,所有的bean都是单例的,然后将mySimpleBean
的作用域从单例改为原型。在实际应用中,我们可能会在 postProcessBeanFactory
方法内部执行更复杂的操作,例如修改 bean 的属性、对Bean对象进行代理做功能增强处理、更改它们的作用域或添加新的 bean 定义等。
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("修改bean的定义");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("mySimpleBean");
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
System.out.println("将mySimpleBean从默认的单例修改成多例");
System.out.println("修改bean的定义已完成");
}
}
运行结果发现,由于mySimpleBean
现在是原型作用域,[com.xcs.spring.config.MySimpleBean@11392934]
和[com.xcs.spring.config.MySimpleBean@6892b3b6]
将是两个不同的地址,说明MySimpleBean
的两个实例是不同的。
修改bean的定义
将mySimpleBean从默认的单例修改成多例
修改bean的定义已完成
MySimpleBean instance: com.xcs.spring.config.MySimpleBean@11392934
MySimpleBean instance: com.xcs.spring.config.MySimpleBean@6892b3b6
首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext
(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration
组件类。从Spring上下文中获取MySimpleBean的两个实例,调用这两个实例的show方法,如果MySimpleBean是单例的,那么这两个实例应该是同一个对象,反之则不是。
public class BeanFactoryPostProcessorApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MySimpleBean mySimpleBean1 = context.getBean(MySimpleBean.class);
MySimpleBean mySimpleBean2 = context.getBean(MySimpleBean.class);
mySimpleBean1.show();
mySimpleBean2.show();
}
}
在org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext
构造函数中,执行了三个步骤,我们重点关注refresh()
方法。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
在org.springframework.context.support.AbstractApplicationContext#refresh
方法中我们重点关注一下finishBeanFactoryInitialization(beanFactory)
这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。
@Override
public void refresh() throws BeansException, IllegalStateException {
// ... [代码部分省略以简化]
// 调用在上下文中注册为bean的工厂处理器
invokeBeanFactoryPostProcessors(beanFactory);
// ... [代码部分省略以简化]
}
在org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法中,又委托了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
进行调用。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ... [代码部分省略以简化]
}
在org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
方法中,主要是对BeanDefinitionRegistryPostProcessor
,BeanFactoryPostProcessor
这两个接口的实现类进行回调,至于为什么这个方法里面代码很长呢?其实这个方法就做了一个事就是对处理器的执行顺序在做处理。比如说要先对实现了PriorityOrdered.class
类回调,在对实现了Ordered.class
类回调,最后才是对没有实现任何优先级的处理器进行回调。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// ... [代码部分省略以简化]
// 获取所有实现了BeanFactoryPostProcessor接口的bean的名称。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// 创建集合以区分不同类型的BeanFactoryPostProcessors
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
// 对BeanFactoryPostProcessors进行分类
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// 如果这个bean已经被处理,直接跳过
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 优先排序的BeanFactoryPostProcessors
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
// 有排序的BeanFactoryPostProcessors
orderedPostProcessorNames.add(ppName);
}
else {
// 没有排序的BeanFactoryPostProcessors
nonOrderedPostProcessorNames.add(ppName);
}
}
// 调用实现了PriorityOrdered接口的BeanFactoryPostProcessors
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 调用实现了Ordered接口的BeanFactoryPostProcessors
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 调用其他所有的BeanFactoryPostProcessors
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除元数据缓存,因为BeanFactoryPostProcessors可能已修改bean定义
beanFactory.clearMetadataCache();
}
下面是我画的一个关于BeanFactoryPostProcessor
排序回调过程时序图大家可以参考一下。
在org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
方法中,循环调用了实现BeanFactoryPostProcessor
接口中的postProcessBeanFactory(registry)
方法
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}
最后执行到我们自定义的逻辑中,在实际应用中,我们可以在 postProcessBeanFactory
方法内部执行更复杂的操作,例如修改 bean 的属性、对Bean对象进行代理做功能增强处理、更改它们的作用域或添加新的 bean 定义等。
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("修改bean的定义");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("mySimpleBean");
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
System.out.println("将mySimpleBean从默认的单例修改成多例");
System.out.println("修改bean的定义已完成");
}
}
考虑其他的BeanFactoryPostProcessor
BeanFactoryPostProcessor
。我们需要确保它们不会互相冲突或导致不一致的bean定义。注意执行顺序
BeanFactoryPostProcessor
,它们的执行顺序可能会影响结果。使用Ordered
接口或PriorityOrdered
接口来明确设置执行顺序。避免循环依赖
BeanDefinition
构造函数,等等情况都有可能引入循环依赖。不要过度使用
BeanFactoryPostProcessor
是一个非常强大接口,但这并不意味着我们就应该频繁使用它。只在真正需要的时候使用它,并考虑是否有其他更简单、更直观的方法可以达到同样的目的。谨慎使用
BeanFactoryPostProcessor
是一个非常强大接口,允许我们修改bean的定义。这意味着我们可以更改bean的类、作用域、属性等。我们要在做这些更改时要非常小心,想想为什么要修改?影响的范围有多少?,以免引入不一致或不可预测的行为。初始化与配置
AnnotationConfigApplicationContext
, 我们成功地启动了Spring容器并加载了MyConfiguration
配置。在MyConfiguration
中, 我们定义了两个核心bean:MySimpleBean
和MyBeanFactoryPostProcessor
.修改Bean的作用域
MySimpleBean
默认是单例,但通过MyBeanFactoryPostProcessor
,我们改变了这一默认行为,将其转变为原型作用域。这种转变是通过覆盖postProcessBeanFactory
方法并更改mySimpleBean
的bean定义来完成的。验证修改
MySimpleBean
的两个实例并调用它们的show
方法时,输出的实例地址明确地告诉我们这两个bean是不同的实例。启动与上下文
AnnotationConfigApplicationContext
,我们初始化了Spring容器,并加载了MyConfiguration
作为主要配置。核心调用
refresh
方法中,invokeBeanFactoryPostProcessors(beanFactory)
确保所有的BeanFactoryPostProcessor
得到适当的调用。回调顺序
BeanFactoryPostProcessor
接口的bean不是随机调用的。Spring确保它们按照PriorityOrdered
、Ordered
和无顺序的层次结构进行分类和调用。自定义逻辑
BeanFactoryPostProcessor
接口并提供自定义逻辑时(例如更改Bean的作用域),该逻辑将在上述过程的适当阶段被调用。具体实践
BeanFactoryPostProcessor
在bean实例化前更改bean定义。在我们的例子中,MySimpleBean
的作用域从单例被更改为原型。此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。