腾讯二面:@Bean与@Component用在同一个类上,会怎么样?
文章来源:https://sourl.cn/nN9qXE
疑虑背景
| 疑虑描述
最近,在进行开发的过程中,发现之前的一个写法,类似如下:
以我的理解,@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 +@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-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级别的日志提示,同时会将@Configuration+@Bean修饰的 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,我想大家应该已经清楚了。
腾讯二面:@Bean与@Component用在同一个类上,会怎么样?相关推荐
- 腾讯二面:@Bean 与 @Component 用在同一个类上,会怎么样?
点击关注公众号,实用技术文章及时了解 来源:cnblogs.com/youzhibing/p/15354706.html 疑虑背景 疑虑描述 最近,在进行开发的过程中,发现之前的一个写法,类似如下 以 ...
- Spring学习(二)--装配Bean
一.Spring装配机制 Spring提供了三种主要的装配机制: 1.在XML中进行显示配置 2.在Java中进行显示配置 3.隐式的bean发现机制和自动装配 --自动化装配bean Spring可 ...
- 一次性讲清 Spring 常用注解 @Bean 、 @Component 、@Autowire、@Resource 的区别, 你知道吗?
本文打算介绍几个不太容易说出其区别,或者用途的 Spring 注解,比如 @Component 与 @Bean 的比较,@ControllerAdvice 是如何处理自定义异常的等等. Spring ...
- Spring 注解 @bean 和 @component 的区别, 你知道吗?
本文打算介绍几个不太容易说出其区别,或者用途的 Spring 注解,比如 @Component 与 @Bean 的比较,@ControllerAdvice 是如何处理自定义异常的等等. Spring ...
- spring二:装配bean(自动装配)
创建应用对象之间协作关系的行为通常称为装配(wiring),这就是依赖注入(DI)的本质. Spring提供了三种主要的装配机制: 1. 在xml中进行显式配置. 2. 在java中进行显式配置. ...
- 腾讯二面挂了,就因为这个...
牛年跳槽季,惨遭开门黑,谨以此文纪念我的首次腾讯面试经历.经我的老师,微软MVP大佬推荐,有幸拿到了腾讯.NET Core高开面试机会,二面却挂在一个最常见的问题上,"你上家公司电商平台的T ...
- 腾讯二十周年:弱冠之礼 穷则思变
腾讯二十周年:弱冠之礼 穷则思变 https://blog.csdn.net/LrS62520kV/article/details/84038908 文章经授权转载自iFeng科技(ID:ifeng_ ...
- 腾讯二面:MySQL的半同步是什么?
前言 年后在进行腾讯二面的时候,写完算法的后问的第一个问题就是,MySQL的半同步是什么?我当时直接懵了,我以为是问的MySQL的两阶段提交的问题呢?结果确认了一下后不是两阶段提交,然后面试官看我连问 ...
- 腾讯二次创业:ToB背后的云起十年|钛媒体深度
关注ITValue,看企业级最新鲜.最价值报道! ▎这一次,腾讯找到ToB的正确姿势了吗? 我们在什么时候会用到腾讯? 聊天打开微信的时候,玩儿<王者荣耀>的时候,还是用腾讯视频追剧刷爱豆 ...
最新文章
- Gartner:人工智能将促使部分专业工作转型
- 厉害了我的VR!老师们用VR来教历史
- min-width、max-width兼容IE6、IE7的解决方法
- 4.2 算法之数论 9274 beeline(python)
- 算法_EXCEL中 A表示第一列,B表示第二列...AA表示27列,AB表示28列,问随意一组字母是多少列
- [渝粤教育] 南京中医药大学 诊断学技能训练 参考 资料
- hdu 5294 Tricks Device 最短路建图+最小割
- 手机APP应用开发技术课程论文
- WPF之NPOIE导出xcel
- 深度学习之图像分类(十二)--MobileNetV3 网络结构
- jbb是什么梗_太阳星座是什么意思
- Tet3在前脑神经元中对抗焦虑
- 视频号如何引流?怎样利用视频号引流?视频号引流的方法和技巧
- AT1219 [JOI2013]歴史の研究
- 【C 语言】结构体 ( 结构体深拷贝 )
- IE被www 537 com www COXDX INFO劫持,修复
- 同步压缩变换在时频分析和盲源分离等方面的应用
- 如何将日期格式转换为英文格式
- gops 是怎么和 Go 的运行时进行交互的?
- Android来电秀应用
热门文章
- noj数据结构稀疏矩阵的加法十字链表_数据结构学习(C )稀疏矩阵(十字链表1)
- java oracle数据回滚,误操作ORACLE生产数据利用闪回查询备份恢复数据-java-51CTO博客...
- python第三方库jieba下载_Python第三方库____jieba
- vb error bc30469: 对非共享成员的引用要求对象引用_提问||准备好面向你的对象了吗?...
- php如何操作文本框,php如何清除文本框
- 了解下C# 基本语法
- 使用ssh连接到centos7中docker容器
- GCC 参数列举及解释
- 在 Ubuntu 上安装最新版本的 Erlang方法介绍
- 中tr不能显示字符_BeautifulSoup4中find 和find_all的比较