diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 03145b6d1bc..e22edafb9b0 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -27,6 +27,8 @@ import org.springframework.aop.scope.ScopedProxyFactoryBean; import org.springframework.asm.Type; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.SimpleInstantiationStrategy; @@ -303,7 +305,7 @@ class ConfigurationClassEnhancer { "result in a failure to process annotations such as @Autowired, " + "@Resource and @PostConstruct within the method's declaring " + "@Configuration class. Add the 'static' modifier to this method to avoid " + - "these container lifecycle issues; see @Bean javadoc for complete details", + "these container lifecycle issues; see @Bean javadoc for complete details.", beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName())); } return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); @@ -318,8 +320,23 @@ class ConfigurationClassEnhancer { if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } - return (!ObjectUtils.isEmpty(beanMethodArgs) ? + Object beanInstance = (!ObjectUtils.isEmpty(beanMethodArgs) ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); + if (beanInstance != null && !beanMethod.getReturnType().isInstance(beanInstance)) { + String msg = String.format("@Bean method %s.%s called as a bean reference " + + "for type [%s] but overridden by non-compatible bean instance of type [%s].", + beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(), + beanMethod.getReturnType().getName(), beanInstance.getClass().getName()); + try { + BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName); + msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription(); + } + catch (NoSuchBeanDefinitionException ex) { + // Ignore - simply no detailed message then. + } + throw new IllegalStateException(msg); + } + return beanInstance; } finally { if (alreadyInCreation) { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 55230889c12..251fcabd0b1 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -298,8 +298,45 @@ public class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); - assertTrue(beanFactory.getBean(Foo.class) instanceof ExtendedFoo); - beanFactory.getBean(Bar.class); + + Foo foo = beanFactory.getBean(Foo.class); + assertTrue(foo instanceof ExtendedFoo); + Bar bar = beanFactory.getBean(Bar.class); + assertSame(foo, bar.foo); + } + + @Test + public void configurationClassesWithValidOverridingForProgrammaticCall() { + beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingAgainSingletonBeanConfig.class)); + beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); + beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + + Foo foo = beanFactory.getBean(Foo.class); + assertTrue(foo instanceof ExtendedAgainFoo); + Bar bar = beanFactory.getBean(Bar.class); + assertSame(foo, bar.foo); + } + + @Test + public void configurationClassesWithInvalidOverridingForProgrammaticCall() { + beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(InvalidOverridingSingletonBeanConfig.class)); + beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class)); + beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + + try { + beanFactory.getBean(Bar.class); + fail("Should have thrown BeanCreationException"); + } + catch (BeanCreationException ex) { + assertTrue(ex.getMessage().contains("OverridingSingletonBeanConfig.foo")); + assertTrue(ex.getMessage().contains(ExtendedFoo.class.getName())); + assertTrue(ex.getMessage().contains(Foo.class.getName())); + assertTrue(ex.getMessage().contains("InvalidOverridingSingletonBeanConfig")); + } } @Test @@ -311,6 +348,7 @@ public class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("consumer", new RootBeanDefinition(ScopedProxyConsumer.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); + ITestBean injected = beanFactory.getBean("consumer", ScopedProxyConsumer.class).testBean; assertTrue(injected instanceof ScopedObject); assertSame(beanFactory.getBean("scopedClass"), injected); @@ -549,13 +587,11 @@ public class ConfigurationClassPostProcessorTests { @Order(1) static class SingletonBeanConfig { - public @Bean - Foo foo() { + public @Bean Foo foo() { return new Foo(); } - public @Bean - Bar bar() { + public @Bean Bar bar() { return new Bar(foo()); } } @@ -564,23 +600,40 @@ public class ConfigurationClassPostProcessorTests { @Order(2) static class OverridingSingletonBeanConfig { - public @Bean - ExtendedFoo foo() { + public @Bean ExtendedFoo foo() { return new ExtendedFoo(); } - public @Bean - Bar bar() { + public @Bean Bar bar() { return new Bar(foo()); } } + @Configuration + static class OverridingAgainSingletonBeanConfig { + + public @Bean ExtendedAgainFoo foo() { + return new ExtendedAgainFoo(); + } + } + + @Configuration + static class InvalidOverridingSingletonBeanConfig { + + public @Bean Foo foo() { + return new Foo(); + } + } + static class Foo { } static class ExtendedFoo extends Foo { } + static class ExtendedAgainFoo extends ExtendedFoo { + } + static class Bar { final Foo foo;