前言
不建議寫這麼奇葩的代碼!!!
這就有點像考試喜歡出的試題,有一堆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的狀況也沒認爲是同一配置類,不會覆蓋。