腾讯二面:@Bean 与 @Component 用在同一个类上,会怎么样?

简介: 腾讯二面:@Bean 与 @Component 用在同一个类上,会怎么样?

疑虑背景


疑虑描述


最近,在进行开发的过程中,发现之前的一个写法,类似如下:


1.png


以我的理解,@Configuration 加 @Bean 会创建一个 userName 不为 null 的 UserManager 对象,而 @Component 也会创建一个 userName 为 null 的 UserManager 对象。


那么我们在其他对象中注入 UserManager 对象时,到底注入的是哪个对象?


因为项目已经上线了很长一段时间了,所以这种写法没有编译报错,运行也没有出问题。后面去找同事了解下,实际是想让:


2.png


生效,而实际也确实是它生效了。那么问题来了:Spring 容器中到底有几个 UserManager 类型的对象?


Spring Boot 版本


项目中用的 Spring Boot 版本是:2.0.3.RELEASE。对象的 scope 是默认值,也就是 singleton。


Spring Boot 基础就不介绍了,推荐下这个实战教程:


https://github.com/javastacks/spring-boot-best-practice


结果验证


验证方式有很多,可以 debug 跟源码,看看 Spring 容器中到底有几个 UserManager 对象,也可以直接从 UserManager 构造方法下手,看看哪几个构造方法被调用,等等。


我们从构造方法下手,看看 UserManager 到底实例化了几次。


3.gif


只有有参构造方法被调用了,无参构造方法岿然不动(根本没被调用)。如果想了解的更深一点,可以读读:Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗?


https://www.cnblogs.com/youzhibing/p/14337244.html


既然 UserManager 构造方法只被调用了一次,那么前面的问题:到底注入的是哪个对象。答案也就清晰了,没得选了呀,只能是 @Configuration 加 @Bean 创建的 userName 不为 null 的 UserManager 对象。


问题又来了:为什么不是 @Component 创建的 userName 为 null 的 UserManager 对象?


源码解析


@Configuration 与 @Component 关系很紧密。


4.png


所以@Configuration能够被component scan。


其中 ConfigurationClassPostProcessor与@Configuration 息息相关,其类继承结构图如下:


5.png


它实现了BeanFactoryPostProcessor接口和PriorityOrdered接口。


关于 BeanFactoryPostProcessor,可以看看:


https://www.cnblogs.com/youzhibing/p/10559337.html


从AbstractApplicationContext的refresh方法调用的invokeBeanFactoryPostProcessors(beanFactory)开始,来跟下源码。


6.gif


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


7.png


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


8.gif9.png


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


10.gif


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 Boot 基础教程及实战示例:


https://github.com/javastacks/spring-boot-best-practice


我们来具体看看。


12.gif


启动直接报错,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的区别。


13.gif


新增了配置项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覆盖。


14.png


Spring4.1.2引进isAllowBeanDefinitionOverriding()方法。


15.png


Spring自始至终默认都是允许BeanDefinition覆盖的,变的是Spring Boot ,Spring Boot 2.1.0之前没有覆盖Spring 的allowBeanDefinitionOverriding默认值,仍是允许BeanDefinition覆盖的。


Spring Boot 2.1.0中SpringApplication定义了私有属性:allowBeanDefinitionOverriding。


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


17.gif


关于allowBeanDefinitionOverriding,我想大家应该已经清楚了。


相关文章
|
存储 网络安全 PHP
在阿里云服务器上如何搭建网站,网址怎么建站图文教程详解案例及步骤.
做好一个网站不仅需要我们对站点装修及内容发布,也需要我们学会对网站运营,如进行站长推送,将我们内容快速推送到各大搜索平台,有效的让用户能搜索到我们内容,或者需要在谷歌推广就必须对网站添加SSL证书,这样搜索域名的时候搜索框不会出现<不安全>字符在域名前面,以及运行网站要懂运维,出现BUG时要去及时解决查找原因.自始至终自身要不断学习网络相关知识,遇到问题方能迎刃而解. 本文结束,如还有不懂的同学可联系作者,倾力而为,祝您成功!
2496 75
|
10天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3248 9
|
3天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
13天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
3293 23
|
7天前
|
人工智能 Linux BI
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
JeecgBoot AI专题研究 一键脚本:Claude Code + JeecgBoot Skills + DeepSeek 全平台接入 一行命令装好 Claude Code + JeecgBoot Skills + DeepSeek 接入,无需翻墙使用 Claude Code,支持 Wind
2311 4
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
|
25天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23597 15
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
12天前
|
人工智能 JSON BI
DeepSeek V4-Pro 接入 Claude Code 完全实战:体验、测试与关键避坑指南
Claude Code 作为当前主流的 AI 编程辅助工具,凭借强大的代码理解、工程执行与自动化能力深受开发者喜爱,但原生模型的使用成本相对较高。为了在保持能力的同时进一步降低开销,不少开发者开始寻找兼容度高、价格更友好的替代模型。DeepSeek V4 系列的发布带来了新的选择,该系列包含 V4-Pro 与 V4-Flash 两款模型,并提供了与 Anthropic 完全兼容的 API 接口,理论上只需简单修改配置,即可让 Claude Code 无缝切换为 DeepSeek 引擎。
2788 3
|
4天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全+三种模式+记忆体系+实战工作流完整手册
Claude Code 是当前最流行的终端级 AI 编程助手,能够直接在命令行中完成代码生成、项目理解、文件修改、命令执行、错误修复等全流程开发工作。它不依赖图形界面、不占用额外资源,却能深度理解项目结构,自动生成规范代码,大幅提升研发效率。
866 2

热门文章

最新文章