Sping中@Configuration@Bean若是出現bean的覆蓋,會怎麼處理?

前言

不建議寫這麼奇葩的代碼!!!
這就有點像考試喜歡出的試題,有一堆overload和override的代碼,選擇題選擇調用的是哪一個。
不建議寫這種讓人看着費勁的代碼。java

問題引出

言歸正傳,若是有一個這樣的配置類,@Bean 註解了相同name = "cupcake"的bean:spring

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());ide

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的過程當中有一步:.net

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

在這個方法中會判斷如今的beanName在現有的beanDefinitionMap中是否已存在,而後決定是否覆蓋。是否覆蓋的策略以下org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.isOverriddenByExistingDefinition(BeanMethod, String)code

// 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。get

深刻

若是是下面這種配置和測試:源碼

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());
    }
    
}

很顯然測試能經過,即會覆蓋。it

@Import呢?

若是是這種狀況呢?io

@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的狀況也沒認爲是同一配置類,不會覆蓋。

相關文章
相關標籤/搜索