服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

@Bean 与 @Component 用在同一个类上,会怎么样?

日期: 来源:SpringForAll收集编辑:
关注我,回复关键字“spring”
免费领取Spring学习资料

来源:cnblogs.com/youzhibing/p/15354706.html

疑虑背景

疑虑描述

最近,在进行开发的过程中,发现之前的一个写法,类似如下
图片
以我的理解,@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-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 覆盖
图片
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 ,我想大家应该已经清楚了


END



如何用 Jenkins+Docker 实现一键自动化部署
Spring Boot + MDC 实现全链路调用日志跟踪
MyBatis 如何实现流式查询
Kubernetes 上 Java 应用的最佳实践

关注后端面试那些事,回复【2022面经】

获取最新大厂Java面经

最后重要提示:高质量的技术交流群,限时免费开放,今年抱团最重要。想进群的,关注SpringForAll社区,回复关键词:加群,拉你进群。

点击这里领取2023大厂面经

相关阅读

  • Pandas 2.0 版本要来了

  • ↓推荐关注↓来源:数据STUDIOPandas[1]是一个用于处理数据的Python库,在Python开发者中非常流行。相信你已经对他非常熟悉了。随着现在数据量越来越多,pandas的局限性也日渐凸
  • Spring Boot 项目如何按模块进行拆分?

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。在了解SpringBoot项目拆分之前首先我们需要了解微服务架构什么是微服务?单个轻量级服务一般为一个单独微服务,微服务讲究
  • 一款高颜值的MySQL管理工具:Sequel Pro

  • Sequel Pro 是一个 Mac 系统上简单易用的 MySQL 和 MariaDB 数据库管理系统。服务器和本地都是Mysql数据库使用的工具:Sequel Pro(专门管理Mysql的工具)操作系统Mac OS 10.12
  • 没有退休金的父母,计划卖房养老

  • 本文系读者来稿,来稿请投至:zhuangao2@lifeweek.com.cn文|读者:小米粥近日,微信家人群里收到父母要卖房子的消息。其实父母也并不是急于要出售房子,他们更多的是在征求我们子女的
  • 武大代表委员在两会!

  • 全国政协十四届一次会议和十四届全国人大一次会议分别于2023年3月4日、5日在京召开。来自武汉大学的两会代表委员们履行庄严使命,发挥专业优势,把握时代脉搏,反映师生心声,围绕
  • 迟日大委员建议:加快编纂环境法典进程

  • 在今年的全国两会上,“生态”一词15次出现在政府工作报告中,充分体现了党中央、国务院对生态文明建设的高度重视。那么,如何用最严格制度最严密法治保护生态环境呢?近日,全国政协
  • “书·法”系列之宪法篇(13)

  • 书·法当书法遇上法律,会擦出怎样的火花?2020年7月11日,在司法部普法与依法治理局指导下,由法治日报社主办、法报文化传媒(北京)有限公司和北京千音文化传媒有限公司联合承办的“

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • SecWiki周刊(第471期)

  • 本期关键字:DNS流量 、社区反作弊、第三方库评估、前出狩猎、异构溯源图、韧性网络安全、安全知识图谱、后渗透、未知网络威胁检测、流量镜像、态势综述、镜像扫描、RSA常见
  • Pandas 2.0 版本要来了

  • ↓推荐关注↓来源:数据STUDIOPandas[1]是一个用于处理数据的Python库,在Python开发者中非常流行。相信你已经对他非常熟悉了。随着现在数据量越来越多,pandas的局限性也日渐凸
  • @Bean 与 @Component 用在同一个类上,会怎么样?

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。来源:cnblogs.com/youzhibing/p/15354706.html疑虑背景疑虑描述最近,在进行开发的过程中,发现之前的一个写法,类似如下图片
  • Spring Boot 项目如何按模块进行拆分?

  • 关注我,回复关键字“spring”,免费领取Spring学习资料。在了解SpringBoot项目拆分之前首先我们需要了解微服务架构什么是微服务?单个轻量级服务一般为一个单独微服务,微服务讲究
  • 某OA系统SYSTEM权限SQL注入

  • 声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,未经授权,严禁转载,如需转载,联系开白。请勿利用文章内的相关技术从事非法测试
  • 每周安全动态精选(3.6-3.10)

  • 本周精选Microsoft Word远程代码执行漏洞Mustang Panda 的最新后门利用 Qt 和 MQTT 开辟了新天地宏碁数据泄露:黑客声称出售 160GB 的被盗数据微软发现 Shein App 在 Android