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

简介: 腾讯二面:@Bean 与 @Component 用在同一个类上,会怎么样?
  • 疑虑背景
  • 疑虑描述
  • Spring Boot 版本
  • 结果验证
  • 源码解析
  • Spring 升级优化
  • 总结
  • 补充

疑虑背景

疑虑描述

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

微信图片_20220906145328.png

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

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

因为项目已经上线了很长一段时间了,所以这种写法没有编译报错,运行也没有出问题

后面去找同事了解下,实际是想让

微信图片_20220906145334.png

生效,而实际也确实是它生效了

那么问题来了:Spring 容器中到底有几个 UserManager 类型的对象?

Spring Boot 版本

项目中用的 Spring Boot 版本是:2.0.3.RELEASE

对象的 scope 是默认值,也就是 singleton

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

结果验证

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

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

image.gif

只有有参构造方法被调用了,无参构造方法岿然不动(根本没被调用)

既然 UserManager 构造方法只被调用了一次,那么前面的问题:到底注入的是哪个对象

答案也就清晰了,没得选了呀,只能是 @Configuration@Bean 创建的 userName 不为 null 的 UserManager 对象

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

基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。

项目地址:https://github.com/YunaiV/onemall

源码解析

@Configuration@Component 关系很紧密

微信图片_20220906145421.png

所以@Configuration 能够被 component scan

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

微信图片_20220906145510.png

它实现了 BeanFactoryPostProcessor 接口和 PriorityOrdered 接口,关于 BeanFactoryPostProcessor

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

image.gif

此时完成了 com.lee.qsl 包下的 component scancom.lee.qsl 包及子包下的 UserConfig 、 UserController 和 UserManager 都被扫描出来

注意,此刻@Bean 的处理还未开始, UserManager 是通过@Component 而被扫描出来的;此时 Spring 容器中 beanDefinitionMap 中的 UserManager 是这样的

微信图片_20220906145545.png

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

微信图片_20220906145605.gif

微信图片_20220906145607.png

循环递归处理 UserConfig 、 UserController 和 UserManager ,把它们都封装成 ConfigurationClass ,递归扫描 BeanDefinition

循环完之后,我们来看看 configClasses

微信图片_20220906145632.gif

UserConfig bean 定义信息中 beanMethods 中有一个元素 [BeanMethod:name=userManager,declaringClass=com.lee.qsl.config.UserConfig]

然后我们接着往下走,来仔细看看答案出现的环节

image.gif

是不是有什么发现?@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 )做出了优化处理

我们来具体看看

微信图片_20220906145712.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 的区别

image.gif

新增了配置项 allowBeanDefinitionOverriding 来控制是否允许 BeanDefinition 覆盖,默认情况下是不允许的

我们可以在配置文件中配置:spring.main.allow-bean-definition-overriding=true ,允许 BeanDefinition 覆盖

这种处理方式是更优的,将选择权交给开发人员,而不是自己偷偷的处理,已达到开发者想要的效果

总结

Spring 5.0.7.RELEASESpring 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 覆盖

微信图片_20220906145820.png

Spring 4.1.2 引进了 isAllowBeanDefinitionOverriding()方法

微信图片_20220906145824.png

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

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

微信图片_20220906145848.png

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

image.gif

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

相关文章
|
搜索推荐 云计算
在线教育平台
在线教育平台
1259 3
|
新零售 人工智能 安全
云上未来,数智导航:阿里云研究院报告合集
阿里云研究院,甄选了2021-2022年度的10份重磅报告,分别从数字经济、行业转型、数字县域等领域,尝试解读、并推动各行各业的转型升级,展望中国数字经济的未来,迎接数字经济发展的春天。
云上未来,数智导航:阿里云研究院报告合集
|
存储 Java 测试技术
阿里巴巴java开发手册
这篇文章是关于阿里巴巴Java开发手册的整理,内容包括编程规约、异常日志、单元测试、安全规约、MySQL数据库使用以及工程结构等方面的详细规范和建议,旨在帮助开发者编写更加规范、高效和安全的代码。
|
机器学习/深度学习 存储 人工智能
未来的移动通信网络,6G与人工智能的融合
在最近的几十年中,移动通信网络从1G发展到6G,通信关键技术层出不穷、迅速发展,广泛应用在人类社会的各行各业,成为社会信息化变革的重要支撑。
1398 0
未来的移动通信网络,6G与人工智能的融合
|
搜索推荐 程序员 SEO
程序员怎么接私活:外包众包接单方法!
程序员怎么接私活:外包众包接单方法!
|
C++ C语言 Linux
Visual Studio Code编写C/C++代码常见问题
我会把一些常见问题以及自己编写代码过程中遇到的问题以及解决方案放在这里,各位若是遇到的问题也可以在评论区留言。 一、头文件Error 不会影响编译运行,但会报Warm,如下图 解决方案是安装Include Autocomplete扩展。
2905 0
|
JSON NoSQL Java
快速搭建一个网关服务
快速搭建一个网关服务,动态路由、鉴权看完就会!
406 0
|
存储 云计算
云计算的概念及相关特点
云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可配置的计算资源共享池(资源包括:网络、服务器、存储、应用软件、服务),这些资源能够被快速提供,只需提供很少的管理工作,或与服务供应商进行很少的交互。   长定义是:云计算是一种商业计算模型。它将计算任务分布在大量计算机构成的资源池上,使各种应用系统能够根据需要获取计算力、存储空间和信息服务。   短定义是:云计算是通过网络按需提供可动态伸缩的廉价计算服务。提供资源的网络被称为“云”。“云”中的资源在使用者看来是可以无限扩展的,并且可以随时获取,按需使用,随时扩展,按使用付费。
STP生成树协议的介绍
STP是一种网络上使用非常广泛的技术。现在已经衍生了STP,RSTP,MSTP。
932 0
STP生成树协议的介绍
|
安全 数据建模 网络安全
如何申请阿里云免费SSL证书(可用于https网站)并下载下来
前提条件:你要有阿里云的账号,并且要有一个域名。注意:阿里云系统也在不断更新,界面以后可能会有稍许变化,但是原理是相通的。 具体步骤: 1.登录到阿里云后台,并选择 “SSL证书(应用安全)” 菜单 2.点击购买证书按钮 3.选择免费型DV SSL,Symantec,点击立即购买按钮。
10255 1