Sping中@Configuration@Bean如果出现bean的覆盖,会怎么处理?

Stella981
• 阅读 776

前言

不建议写这么奇葩的代码!!!
这就有点像考试喜欢出的试题,有一堆overload和override的代码,选择题选择调用的是哪个。
不建议写这种让人看着费劲的代码。

问题引出

言归正传,如果有一个这样的配置类,[@Bean](https://my.oschina.net/bean) 注解了相同name = "cupcake"的bean:

public class BeanOverrideConfig {
    @Bean(name = "cupcake")
    public Cupcake cupcake1() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake1");
        return cupcake;
    }
    
    @Bean(name = "cupcake")
    public Cupcake cupcake2() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake2");
        return cupcake;
    }
    
}

下面这个测试类能通过测试吗?注意最后一行代码Assert.assertEquals("Cupcake1", cupcake.getName());

public class BeanOverrideTest {
    private ApplicationContext ctx = null;
    
    @Before
    public void setUp() {
        ctx = new AnnotationConfigApplicationContext(BeanOverrideConfig.class);
    }
    
    @Test
    public void testGetBean() {
        Cupcake cupcake = ctx.getBean(Cupcake.class);
        Assert.assertNotNull(cupcake);
        Assert.assertEquals("Cupcake1", cupcake.getName());
    }
    
}

结果

测试通过!

原因

Spring对configuration class的加载
加载BeanDefinition的过程中有一步:

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(BeanMethod)

在这个方法中会判断现在的beanName在现有的beanDefinitionMap中是否已存在,然后决定是否覆盖。是否覆盖的策略如下org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.isOverriddenByExistingDefinition(BeanMethod, String)

// Is the existing bean definition one that was created from a configuration class?
// -> allow the current bean method to override, since both are at second-pass level.
// However, if the bean method is an overloaded case on the same configuration class,
// preserve the existing bean definition.
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
    ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
    return ccbd.getMetadata().getClassName().equals(
            beanMethod.getConfigurationClass().getMetadata().getClassName());
}

源码里说的很清楚了,如果来自不同层级的bean method,允许覆盖,如果是the same configuration class,preserve the existing bean definition(同一configuration class的overload,保留先前的)。
回到我们的测试类,即保留方法public Cupcake cupcake1()对应的bean definition,最后测试的时候getName就返回Cupcake1。

深入

如果是下面这种配置和测试:

public class BeanOverrideConfig1 {
    @Bean(name = "cupcake")
    public Cupcake cupcake1() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake1");
        return cupcake;
    }
    
}

public class BeanOverrideConfig2 {
    
    @Bean(name = "cupcake")
    public Cupcake cupcake2() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake2");
        return cupcake;
    }
    
}

public class BeanOverrideTest {
    private ApplicationContext ctx = null;
    
    @Before
    public void setUp() {
        ctx = new AnnotationConfigApplicationContext(BeanOverrideConfig1.class, BeanOverrideConfig2.class);
    }
    
    @Test
    public void testOverride() {
        Cupcake cupcake = ctx.getBean(Cupcake.class);
        Assert.assertNotNull(cupcake);
        Assert.assertEquals("Cupcake2", cupcake.getName());
    }
    
}

很显然测试能通过,即会覆盖。

@Import呢?

如果是这种情况呢?

@Import(BeanOverrideConfig2.class)
public class BeanOverrideConfig1 {
    @Bean(name = "cupcake")
    public Cupcake cupcake1() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake1");
        return cupcake;
    }
    
}

public class BeanOverrideConfig2 {
    
    @Bean(name = "cupcake")
    public Cupcake cupcake2() {
        Cupcake cupcake = new Cupcake();
        cupcake.setName("Cupcake2");
        return cupcake;
    }
    
}

public class BeanOverrideTest {
    private ApplicationContext ctx = null;
    
    @Before
    public void setUp() {
        ctx = new AnnotationConfigApplicationContext(BeanOverrideConfig1.class);
    }
    
    @Test
    public void testOverride() {
        Cupcake cupcake = ctx.getBean(Cupcake.class);
        Assert.assertNotNull(cupcake);
        Assert.assertEquals("Cupcake1", cupcake.getName());
    }
    
}

测试通过,这种@Import的情况也没认为是同一配置类,不会覆盖。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Spring 学习笔记(三):Spring Bean
1Bean配置Spring可以看做是一个管理Bean的工厂,开发者需要将Bean配置在XML或者Properties配置文件中。实际开发中常使用XML的格式,其中<bean中的属性或子元素如下:id:Bean在BeanFactory中的唯一标识,在代码中通过BeanFac
Stella981 Stella981
3年前
CAS 实现站内单点登录及实现第三方 OAuth、OpenId 登录(四)
一、OAuth配置1.配置OAuth提供商<bean id"weibo" class"com.buession.oauth.provider.impl.WeiboProvider"    <property name"key" value"the_key_for_
Easter79 Easter79
3年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这