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

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

前言

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

问题引出

言归正传,如果有一个这样的配置类,@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的情况也没认为是同一配置类,不会覆盖。

目录
相关文章
|
25天前
|
Java Shell C++
Springboot加载注入bean的方式
本文详细介绍了Spring Boot中Bean的装配方法。首先讲解了使用@Component、@Service、@Controller、@Repository等注解声明Bean的方式,并解释了这些注解之间的关系及各自适用的层次。接着介绍了通过@Configuration和@Bean注解定义Bean的方法,展示了其灵活性和定制能力。最后讨论了@Component与@Bean的区别,并提供了在Spring Boot应用中装配依赖包中Bean的三种方法:使用@ComponentScan注解扫描指定包、使用@Import注解导入特定Bean以及在spring.factories文件中配置Bean。
|
6月前
|
Java Spring 容器
Bean背后的魔法:揭秘Spring配置Bean的实现原理
Bean背后的魔法:揭秘Spring配置Bean的实现原理
58 0
Bean背后的魔法:揭秘Spring配置Bean的实现原理
|
6月前
|
XML Java 数据格式
Spring Bean的定义(含创建Bean的三种方式)
Spring Bean的定义(含创建Bean的三种方式)
|
6月前
|
Java Spring
flowable 监听器无法获取spring bean ,自动注入bean为null,解决方案
flowable 监听器无法获取spring bean ,自动注入bean为null,解决方案
|
XML Java 数据格式
如何正确控制springboot中bean的加载顺序总结
springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题。在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功能。 在一般业务场景,可能你不大关心一个bean是如何被注册进spring容器的。只需要把需要注册进容器的bean声明为@Component即可,spring会自动扫描到这个Bean完成初始化并加载到spring上下文容器。
|
XML Java 数据库
SpringIOC操作Bean管理--最终章
SpringIOC操作Bean管理--最终章
78 0
|
6月前
|
Java 数据库连接 API
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
641 0
|
XML 前端开发 Java
Spring-基于注解的配置[01定义Bean+扫描Bean]
Spring-基于注解的配置[01定义Bean+扫描Bean]
120 0
|
Java 容器 Spring
Spring基础篇:利用注解将外部Properties属性注入到Bean中的方法
利用注解将外部Properties属性注入到Bean中的方法
164 0
|
Java 开发者 Spring
《SpringBoot篇》07.@ConfigurationProperties注解实现第三方bean加载属性
《SpringBoot篇》07.@ConfigurationProperties注解实现第三方bean加载属性
241 0
《SpringBoot篇》07.@ConfigurationProperties注解实现第三方bean加载属性