当前位置: 首页 > 后端技术 > Java

腾讯二面:@Bean和@Component用在同一个类上会怎样?_0

时间:2023-04-01 20:50:56 Java

来源:cnblogs.com/youzhibing/p/15354706.html疑问背景疑问描述最近在开发的过程中,发现了以前的一个写法,类似如下:在我的理解中,@Configuration加上@Bean会创建一个userName不为null的UserManager对象,@Component也会创建一个userName为null的UserManager对象。那么我们在其他对象中注入UserManager对象时,注入的是哪个对象呢?因为项目已经上线很久了,所以这种写法没有编译报错,运行也没有问题。后来去找同事打听。我其实是想让它生效,事实上它确实生效了。那么问题来了:Spring容器中到底有多少个UserManager类型的对象?SpringBoot版本项目中使用的SpringBoot版本为:2.0.3.RELEASE。对象的范围是默认值,即单例。我不会介绍SpringBoot的基础知识。推荐这个实用教程:https://github.com/javastacks...结果验证方式很多。可以通过调试和源码查看Spring容器中有多少个UserManager对象。也可以直接从UserManager的构造方法入手,看看调用了哪些构造方法等等。我们先从构造方法说起,看看UserManager被实例化了多少次。只调用有参构造函数,无参构造函数保持不变(根本不调用)。想深入了解可以阅读:Spring的循环依赖,源码详解→你真的需要三级缓存吗?https://www.cnblogs.com/youzh...由于UserManager的构造函数只被调用一次,前面的问题:注入哪个对象。答案也很明确,没办法,只能是@Configuration加上@Bean创建的UserManager对象,而且userName不为null。问题又来了:为什么@Component不创建userName为null的UserManager对象呢?源码分析@Configuration与@Component关系密切。所以@Configuration可以按组件扫描。其中ConfigurationClassPostProcessor与@Configuration关系密切,其类继承结构图如下:它实现了BeanFactoryPostProcessor接口和PriorityOrdered接口。关于BeanFactoryPostProcessor,可以看:https://www.cnblogs.com/youzh...从AbstractApplicationContext的refresh方法调用的invokeBeanFactoryPostProcessors(beanFactory)开始,跟着源码走。至此com.lee.qsl包下的组件扫描完成,com.lee.qsl包下的UserConfig、UserController、UserManager及子包都扫描完毕。注意此时@Bean的处理还没有开始,通过@Component扫描出UserManager;这时Spring容器中的beanDefinitionMap中的UserManager是这样的。下一步非常重要,与我们想要的答案有很大关系。循环递归处理UserConfig、UserController和UserManager,封装成ConfigurationClass,递归扫描BeanDefinition。循环之后,我们来看一下configClasses。UserConfigbean定义中的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-0320:37:33.697INFO13600---[main]o.s.b.f.s.DefaultListableBeanFactory:Overridingbeandefinitionforbean'userManager'withadifferentdefinition:replacing[Genericbean:类[com.lee.qsl.manager.UserManager];范围=单例;摘要=假;懒惰初始化=假;自动接线模式=0;依赖检查=0;autowireCandidate=真;初级=假;工厂BeanName=null;工厂方法名=空;初始化方法名=空;destroyMethodName=null;在文件[D:\qsl-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.class]]中定义[Rootbean:class[null];范围=;摘要=假;懒惰初始化=假;自动接线模式=3;依赖检查=0;autowireCandidate=真;初级=假;factoryBeanName=用户配置;factoryMethodName=用户管理器;初始化方法名=空;destroyMethodName=(推断);在类路径资源中定义[com/lee/qsl/config/UserConfig.class]]只是日志等级别是info,太不显眼了。Spring升级优化可能是Spring团队意识到info层太不起眼,或者意识到直接覆盖的方式不合理。所以在Spring5.1.2.RELEASE(SpringBoot是2.1.0.RELEASE)优化处理。推荐一个SpringBoot基础教程和实例:https://github.com/javastacks...一起来看看吧。启动直接报错,Spring也给出了提示。类路径资源[com/lee/qsl/config/UserConfig.class]中定义的bean“userManager”无法注册。具有该名称的bean已经在文件[D:\qsl-project\spring-boot-bean-component\target\classes\com\lee\qsl\manager\UserManager.class]中定义并且禁用了覆盖。下面跟着源码走,主要是看看和Spring5.0.7.RELEASE的区别。新增配置项allowBeanDefinitionOverriding,控制是否允许BeanDefinition覆盖,默认不允许。我们可以在配置文件中配置:spring.main.allow-bean-definition-overriding=true来让BeanDefinition覆盖。这种处理方式比较优化,选择权交给了开发者,而不是自己偷偷处理,达到了开发者想要的效果。总结Spring5.0.7.RELEASE(SpringBoot2.0.3.RELEASE)支持@Configuration+@Bean和@Component同时作用于同一个类。启动时会给出info级别的日志提示,同时@Configuration+@Bean修改的BeanDefinition会被@Component修改的BeanDefinition覆盖。可能是Spring团队意识到上述处理不妥,所以在Spring5.1.2.RELEASE中进行了优化。增加了一个配置项:allowBeanDefinitionOverriding,将主动权交给开发者,由开发者决定是否允许覆盖。关于allowBeanDefinitionOverriding的补充,前面有错误,特意去找源码,补充如下。Spring1.2引入DefaultListableBeanFactory时,有privatebooleanallowBeanDefinitionOverriding=true;,默认是允许BeanDefinition覆盖。Spring4.1.2引入了isAllowBeanDefinitionOverriding()方法。Spring一直默认允许BeanDefinition被覆盖,换成SpringBoot。在SpringBoot2.1.0之前,Spring的allowBeanDefinitionOverriding默认值没有被覆盖,仍然允许覆盖BeanDefinition。SpringBoot2.1.0中的SpringApplication定义了一个私有属性:allowBeanDefinitionOverriding。如果没有显示指定值,则默认值为false,然后在SpringBoot启动过程中,将使用该值覆盖Spring中allowBeanDefinitionOverriding的默认值。关于allowBeanDefinitionOverriding,我想大家应该已经很清楚了。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!