Proper exception in case of an @Bean method call encountering a bean type mismatch
Issue: SPR-12905
This commit is contained in:
parent
1da98b0542
commit
e403aefe86
|
|
@ -27,6 +27,8 @@ import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||||
import org.springframework.asm.Type;
|
import org.springframework.asm.Type;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
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.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
||||||
|
|
@ -303,7 +305,7 @@ class ConfigurationClassEnhancer {
|
||||||
"result in a failure to process annotations such as @Autowired, " +
|
"result in a failure to process annotations such as @Autowired, " +
|
||||||
"@Resource and @PostConstruct within the method's declaring " +
|
"@Resource and @PostConstruct within the method's declaring " +
|
||||||
"@Configuration class. Add the 'static' modifier to this method to avoid " +
|
"@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()));
|
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
|
||||||
}
|
}
|
||||||
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
|
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
|
||||||
|
|
@ -318,8 +320,23 @@ class ConfigurationClassEnhancer {
|
||||||
if (alreadyInCreation) {
|
if (alreadyInCreation) {
|
||||||
beanFactory.setCurrentlyInCreation(beanName, false);
|
beanFactory.setCurrentlyInCreation(beanName, false);
|
||||||
}
|
}
|
||||||
return (!ObjectUtils.isEmpty(beanMethodArgs) ?
|
Object beanInstance = (!ObjectUtils.isEmpty(beanMethodArgs) ?
|
||||||
beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName));
|
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 {
|
finally {
|
||||||
if (alreadyInCreation) {
|
if (alreadyInCreation) {
|
||||||
|
|
|
||||||
|
|
@ -298,8 +298,45 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class));
|
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
pp.postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
assertTrue(beanFactory.getBean(Foo.class) instanceof ExtendedFoo);
|
|
||||||
|
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);
|
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
|
@Test
|
||||||
|
|
@ -311,6 +348,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.registerBeanDefinition("consumer", new RootBeanDefinition(ScopedProxyConsumer.class));
|
beanFactory.registerBeanDefinition("consumer", new RootBeanDefinition(ScopedProxyConsumer.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
pp.postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
|
||||||
ITestBean injected = beanFactory.getBean("consumer", ScopedProxyConsumer.class).testBean;
|
ITestBean injected = beanFactory.getBean("consumer", ScopedProxyConsumer.class).testBean;
|
||||||
assertTrue(injected instanceof ScopedObject);
|
assertTrue(injected instanceof ScopedObject);
|
||||||
assertSame(beanFactory.getBean("scopedClass"), injected);
|
assertSame(beanFactory.getBean("scopedClass"), injected);
|
||||||
|
|
@ -549,13 +587,11 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
@Order(1)
|
@Order(1)
|
||||||
static class SingletonBeanConfig {
|
static class SingletonBeanConfig {
|
||||||
|
|
||||||
public @Bean
|
public @Bean Foo foo() {
|
||||||
Foo foo() {
|
|
||||||
return new Foo();
|
return new Foo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Bean
|
public @Bean Bar bar() {
|
||||||
Bar bar() {
|
|
||||||
return new Bar(foo());
|
return new Bar(foo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -564,23 +600,40 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
@Order(2)
|
@Order(2)
|
||||||
static class OverridingSingletonBeanConfig {
|
static class OverridingSingletonBeanConfig {
|
||||||
|
|
||||||
public @Bean
|
public @Bean ExtendedFoo foo() {
|
||||||
ExtendedFoo foo() {
|
|
||||||
return new ExtendedFoo();
|
return new ExtendedFoo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Bean
|
public @Bean Bar bar() {
|
||||||
Bar bar() {
|
|
||||||
return new Bar(foo());
|
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 Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ExtendedFoo extends Foo {
|
static class ExtendedFoo extends Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ExtendedAgainFoo extends ExtendedFoo {
|
||||||
|
}
|
||||||
|
|
||||||
static class Bar {
|
static class Bar {
|
||||||
|
|
||||||
final Foo foo;
|
final Foo foo;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue