当前位置:网站首页>@Bean 与 @Component 用在同一个类上,会发生什么?
@Bean 与 @Component 用在同一个类上,会发生什么?
2022-07-27 13:20:00 【技术琐事】
疑虑描述

最近,在进行开发的过程中,发现之前的一个写法,类似如下
以我的理解,@Configuration 加 @Bean 会创建一个 userName 不为 null 的 UserManager 对象,而 @Component 也会创建一个 userName 为 null 的 UserManager 对象
那么我们在其他对象中注入 UserManager 对象时,到底注入的是哪个对象?
因为项目已经上线了很长一段时间了,所以这种写法没有编译报错,运行也没有出问题
后面去找同事了解下,实际是想让

生效,而实际也确实是它生效了
那么问题来了:Spring 容器中到底有几个 UserManager 类型的对象?
Spring Boot 版本
项目中用的 Spring Boot 版本是:2.0.3.RELEASE
对象的 scope 是默认值,也就是 singleton
结果验证
验证方式有很多,可以 debug 跟源码,看看 Spring 容器中到底有几个 UserManager 对象,也可以直接从 UserManager 构造方法下手,看看哪几个构造方法被调用,等等
我们从构造方法下手,看看 UserManager 到底实例化了几次

只有有参构造方法被调用了,无参构造方法岿然不动(根本没被调用)
既然 UserManager 构造方法只被调用了一次,那么前面的问题:到底注入的是哪个对象
答案也就清晰了,没得选了呀,只能是 @Configuration 加 @Bean 创建的 userName 不为 null 的 UserManager 对象
问题又来了:为什么不是 @Component 创建的 userName 为 null 的 UserManager 对象?
源码解析
@Configuration 与 @Component 关系很紧密

所以@Configuration 能够被 component scan
其中 ConfigurationClassPostProcessor 与@Configuration 息息相关,其类继承结构图如下:

它实现了 BeanFactoryPostProcessor 接口和 PriorityOrdered 接口,关于 BeanFactoryPostProcessor,
那么我们从 AbstractApplicationContext 的 refresh 方法调用的 invokeBeanFactoryPostProcessors(beanFactory)开始,来跟下源码

此时完成了 com.lee.qsl 包下的 component scan , com.lee.qsl 包及子包下的 UserConfig 、 UserController 和 UserManager 都被扫描出来
注意,此刻@Bean 的处理还未开始, UserManager 是通过@Component 而被扫描出来的;此时 Spring 容器中 beanDefinitionMap 中的 UserManager 是这样的

接下来一步很重要,与我们想要的答案息息相关


循环递归处理 UserConfig 、 UserController 和 UserManager ,把它们都封装成 ConfigurationClass ,递归扫描 BeanDefinition
循环完之后,我们来看看 configClasses

UserConfig bean 定义信息中 beanMethods 中有一个元素 [BeanMethod:name=userManager,declaringClass=com.lee.qsl.config.UserConfig]
然后我们接着往下走,来仔细看看答案出现的环节

是不是有什么发现?@Component 修饰的 UserManager 定义直接被覆盖成了 @Configuration + @Bean 修饰的 UserManager 定义
Bean 定义类型也由 ScannedGenericBeanDefinition 替换成了 ConfigurationClassBeanDefinition
后续通过 BeanDefinition 创建实例的时候,创建的自然就是 @Configuration + @Bean 修饰的 UserManager ,也就是会反射调用 UserManager 的有参构造方法
自此,答案也就清楚了
Spring 其实给出了提示
2021-10-03 20:37:33.697 INFO 13600 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'userManager' with a different definition: replacing [Generic bean: class [com.lee.qsl.manager.UserManager]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\qsl-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=userConfig; factoryMethodName=userManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource[com/lee/qsl/config/UserConfig.class]]
只是日志级别是 info ,太不显眼了
Spring 升级优化
可能 Spring 团队意识到了 info 级别太不显眼的问题,或者说意识到了直接覆盖的处理方式不太合理
所以在 Spring 5.1.2.RELEASE (Spring Boot 则是 2.1.0.RELEASE )做出了优化处理
我们来具体看看

启动直接报错,Spring 也给出了提示
The bean 'userManager', defined in class path resource [com/lee/qsl/config/UserConfig.class], could not be registered. A bean with that name has already been defined in file [D:qsl-projectspring-boot-bean-componenttargetclassescomleeqslmanagerUserManager.class] and overriding is disabled.
我们来跟下源码,主要看看与 Spring 5.0.7.RELEASE 的区别

新增了配置项 allowBeanDefinitionOverriding 来控制是否允许 BeanDefinition 覆盖,默认情况下是不允许的
我们可以在配置文件中配置:spring.main.allow-bean-definition-overriding=true ,允许 BeanDefinition 覆盖
这种处理方式是更优的,将选择权交给开发人员,而不是自己偷偷的处理,已达到开发者想要的效果
总结
Spring 5.0.7.RELEASE ( Spring Boot 2.0.3.RELEASE ) 支持@Configuration + @Bean 与@Component 同时作用于同一个类
启动时会给 info 级别的日志提示,同时会将@Configuration + @Bean 修饰的 BeanDefinition 覆盖掉@Component 修饰的 BeanDefinition
也许 Spring 团队意识到了上述处理不太合适,于是在 Spring 5.1.2.RELEASE 做出了优化处理
增加了配置项:allowBeanDefinitionOverriding ,将主动权交给了开发者,由开发者自己决定是否允许覆盖
补充
关于 allowBeanDefinitionOverriding ,前面讲的不对,后面特意去翻了下源码,补充如下
Spring 1.2 引进 DefaultListableBeanFactory 的时候就有了 private boolean allowBeanDefinitionOverriding = true;,默认是允许 BeanDefinition 覆盖

Spring 4.1.2 引进了 isAllowBeanDefinitionOverriding()方法

Spring 自始至终默认都是允许 BeanDefinition 覆盖的,变的是 Spring Boot , Spring Boot 2.1.0 之前没有覆盖 Spring 的 allowBeanDefinitionOverriding 默认值,仍是允许 BeanDefinition 覆盖的
Spring Boot 2.1.0 中 SpringApplication 定义了私有属性:allowBeanDefinitionOverriding

没有显示的指定值,那么默认值就是 false ,之后在 Spring Boot 启动过程中,会用此值覆盖掉 Spring 中的 allowBeanDefinitionOverriding 的默认值

关于 allowBeanDefinitionOverriding ,我想大家应该已经清楚了。
边栏推荐
- RTL8762DK 环境搭建(一)
- codeforces 1708E - DFS Trees
- Negative ring
- Flexible and easy to use WYSIWYG visual report
- spark job 使用log4j appender 追加日志到本地文件或者mysql
- [training day4] sequence transformation [thinking]
- SLAM综述阅读笔记六:基于图像语义的SLAM调研:移动机器人自主导航面向应用的解决方案 2020
- 2022牛客多校二_ E I
- Document translation__ Salt and pepper image denoising based on adaptive total variation L1 regularization
- Leetcode · daily question · 592. fraction addition and subtraction · simulation
猜你喜欢

GoPro access - control and preview GoPro according to GoPro official document /demo

面向不平衡数据的电子病历自动分类研究

Toward Fast, Flexible, and Robust Low-Light Image Enhancement(实现快速、灵活和稳健的弱光图像增强)CVPR2022

Navicate报错access violation at address 00000000

机场云商sign解析

Slam overview Reading Note 6: slam research based on image semantics: application-oriented solutions for autonomous navigation of mobile robots 2020

LeetCode·每日一题·592.分数加减运算·模拟

万字详解 Google Play 上架应用标准包格式 AAB

uniapp的request数据请求简单封装步骤

Group division and characteristic analysis of depression patients based on online consultation records
随机推荐
Leetcode · daily question · 592. fraction addition and subtraction · simulation
SLAM综述阅读笔记四:A Survey on Deep Learning for Localization and Mapping: Towards the Age of Spatial 2020
HDU1422 重温世界杯【DP】
[intensive reading of papers] grounded language image pre training (glip)
Jing Xiandong and other senior executives of ant group no longer serve as Alibaba partners to ensure independent decision-making
解气!哈工大被禁用MATLAB后,国产工业软件霸气回击
Zhishang technology IPO meeting: annual revenue of 600million, book value of accounts receivable of 270Million
Positive mask, negative mask, wildcard
Recursive method to realize the greatest common divisor
Document translation__ Tvreg V2: variational imaging method for denoising, deconvolution, repair and segmentation (part)
Blocking queue
Dako held a meeting for the biological IPO: the annual revenue was 837million, and Wu Qingjun and his daughter were the actual controllers
STM32——电容触摸按键实验
Is the security of online brokerage app account opening guaranteed?
Shell编程规范与变量
PROFINET 模拟器使用教程
[training day3] delete [simulation]
Architecture - the sublimation of MVC
【笔记】逻辑斯蒂回归
@Repository详解