当前位置:网站首页>@Bean与@Component用在同一个类上,会怎么样?
@Bean与@Component用在同一个类上,会怎么样?
2022-07-07 10:04:00 【程序猿DD_】
| 疑虑描述
最近,在进行开发的过程中,发现之前的一个写法,类似如下:

以我的理解,@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 到底实例化了几次。

只有有参构造方法被调用了,无参构造方法岿然不动(根本没被调用)。如果想了解的更深一点,可以读读:Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗?
https://www.cnblogs.com/youzhibing/p/14337244.html
既然 UserManager 构造方法只被调用了一次,那么前面的问题:到底注入的是哪个对象。答案也就清晰了,没得选了呀,只能是 @Configuration 加 @Bean 创建的 userName 不为 null 的 UserManager 对象。
问题又来了:为什么不是 @Component 创建的 userName 为 null 的 UserManager 对象?
源码解析
@Configuration 与 @Component 关系很紧密。

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

它实现了BeanFactoryPostProcessor接口和PriorityOrdered接口。
关于 BeanFactoryPostProcessor,可以看看:
https://www.cnblogs.com/youzhibing/p/10559337.html
从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 [email protected]修饰的UserManager定义。Bean定义类型也由ScannedGenericBeanDefinition替换成了ConfigurationClassBeanDefinition。
后续通过BeanDefinition创建实例的时候,创建的自然就是@[email protected]修饰的 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-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.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级别的日志提示,同时会将@[email protected]修饰的 BeanDefinition覆盖掉@Component修饰的BeanDefinition。
也许Spring团队意识到了上述处理不太合适,于是在Spring 5.1.2.RELEASE做出了优化处理。增加了配置项:allowBeanDefinitionOverriding,将主动权交给了开发者,由开发者自己决定是否允许覆盖。
| 补充
关于allowBeanDefinitionOverriding,前面有误,特意去翻了下源码,补充如下。Spring 1.2引进DefaultListableBeanFactory的时候就有了private boolean allowBeanDefinitionOverriding=true;,默认是允许BeanDefinition覆盖。

Spring4.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,我想大家应该已经清楚了。
我们创建了一个高质量的技术交流群,与优秀的人在一起,自己也会优秀起来,赶紧点击加群,享受一起成长的快乐。另外,如果你最近想跳槽的话,年前我花了2周时间收集了一波大厂面经,节后准备跳槽的可以点击这里领取!
推荐阅读
··································
你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。
边栏推荐
- SwiftUI 4 新功能之掌握 WeatherKit 和 Swift Charts
- 《论文阅读》Neural Approaches to Conversational AI(1)
- Neural approvals to conversational AI (1)
- 【数据聚类】基于多元宇宙优化DBSCAN实现数据聚类分析附matlab代码
- Automated testing framework
- [system design] index monitoring and alarm system
- 竟然有一半的人不知道 for 与 foreach 的区别???
- STM32F1与STM32CubeIDE编程实例-315M超再生无线遥控模块驱动
- Software design - "high cohesion and low coupling"
- HCIA复习整理
猜你喜欢

Onedns helps college industry network security

【最短路】Acwing1128信使:floyd最短路

Talk about SOC startup (VI) uboot startup process II

Complete collection of common error handling in MySQL installation

Improve application security through nonce field of play integrity API

Flet tutorial 17 basic introduction to card components (tutorial includes source code)

Unity中SmoothStep介绍和应用: 溶解特效优化

powershell cs-UTF-16LE编码上线

《通信软件开发与应用》课程结业报告

【紋理特征提取】基於matlab局部二值模式LBP圖像紋理特征提取【含Matlab源碼 1931期】
随机推荐
sql里,我想设置外键,为什么出现这个问题
软件内部的定时炸弹:0-Day Log4Shell只是冰山一角
Stm32f1 and stm32subeide programming example -max7219 drives 8-bit 7-segment nixie tube (based on SPI)
110. Network security penetration test - [privilege promotion 8] - [windows sqlserver xp_cmdshell stored procedure authorization]
Common locking table processing methods in Oracle
【紋理特征提取】基於matlab局部二值模式LBP圖像紋理特征提取【含Matlab源碼 1931期】
R语言使用quantile函数计算评分值的分位数(20%、40%、60%、80%)、使用逻辑操作符将对应的分位区间(quantile)编码为分类值生成新的字段、strsplit函数将学生的名和姓拆分
The Oracle message permission under the local Navicat connection liunx is insufficient
Talk about SOC startup (11) kernel initialization
Nuclear boat (I): when "male mothers" come into reality, can the biotechnology revolution liberate women?
R language uses the quantile function to calculate the quantile of the score value (20%, 40%, 60%, 80%), uses the logical operator to encode the corresponding quantile interval (quantile) into the cla
Flet教程之 16 Tabs 选项卡控件 基础入门(教程含源码)
【神经网络】卷积神经网络CNN【含Matlab源码 1932期】
Enclosed please find. Net Maui's latest learning resources
How to connect 5V serial port to 3.3V MCU serial port?
大佬们有没有人遇到过 flink oracle cdc,读取一个没有更新操作的表,隔十几秒就重复读取
Fleet tutorial 15 introduction to GridView Basics (tutorial includes source code)
[question] Compilation Principle
powershell cs-UTF-16LE编码上线
EasyUI learn to organize notes