diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index b5926e7e3a2..9543ecf46cc 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -27,6 +27,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.annotation.Autowire; @@ -42,6 +43,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; @@ -287,6 +289,12 @@ class ConfigurationClassBeanDefinitionReader { return (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName())); } + // A bean definition resulting from a component scan can be silently overridden + // by an @Bean method, as of 4.2... + if (existingBeanDef instanceof ScannedGenericBeanDefinition) { + return false; + } + // Has the existing bean definition bean marked as a framework-generated bean? // -> allow the current bean method to override it, since it is application-level if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) { @@ -295,6 +303,11 @@ class ConfigurationClassBeanDefinitionReader { // At this point, it's a top-level override (probably XML), just having been parsed // before configuration class processing kicks in... + if (this.registry instanceof DefaultListableBeanFactory && + !((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) { + throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), + beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef); + } if (logger.isInfoEnabled()) { logger.info(String.format("Skipping bean definition for %s: a definition for bean '%s' " + "already exists. This top-level bean definition is considered as an override.", diff --git a/spring-context/src/test/java/example/scannable_implicitbasepackage/ComponentScanAnnotatedConfigWithImplicitBasePackage.java b/spring-context/src/test/java/example/scannable_implicitbasepackage/ComponentScanAnnotatedConfigWithImplicitBasePackage.java index 0df041daacc..1d515dec0ce 100644 --- a/spring-context/src/test/java/example/scannable_implicitbasepackage/ComponentScanAnnotatedConfigWithImplicitBasePackage.java +++ b/spring-context/src/test/java/example/scannable_implicitbasepackage/ComponentScanAnnotatedConfigWithImplicitBasePackage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package example.scannable_implicitbasepackage; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -24,4 +26,10 @@ import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class ComponentScanAnnotatedConfigWithImplicitBasePackage { + + @Bean // override of scanned class + public ConfigurableComponent configurableComponent() { + return new ConfigurableComponent(true); + } + } diff --git a/spring-context/src/test/java/example/scannable_implicitbasepackage/ConfigurableComponent.java b/spring-context/src/test/java/example/scannable_implicitbasepackage/ConfigurableComponent.java new file mode 100644 index 00000000000..3ac78bfeb7e --- /dev/null +++ b/spring-context/src/test/java/example/scannable_implicitbasepackage/ConfigurableComponent.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.scannable_implicitbasepackage; + +import org.springframework.stereotype.Component; + +/** + * @author Juergen Hoeller + */ +@Component +public class ConfigurableComponent { + + private final boolean flag; + + public ConfigurableComponent() { + this(false); + } + + public ConfigurableComponent(boolean flag) { + this.flag = flag; + } + + public boolean isFlag() { + return this.flag; + } + +} diff --git a/spring-context/src/test/java/example/scannable_implicitbasepackage/ScannedComponent.java b/spring-context/src/test/java/example/scannable_implicitbasepackage/ScannedComponent.java index f47e5d3a5d5..5a32fe06b2c 100644 --- a/spring-context/src/test/java/example/scannable_implicitbasepackage/ScannedComponent.java +++ b/spring-context/src/test/java/example/scannable_implicitbasepackage/ScannedComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package example.scannable_implicitbasepackage; import org.springframework.stereotype.Component; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index a3986cd6fe0..c4314401b3d 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import example.scannable.FooService; import example.scannable.MessageBean; import example.scannable.ScopedProxyTestBean; import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage; +import example.scannable_implicitbasepackage.ConfigurableComponent; import example.scannable_scoped.CustomScopeAnnotationBean; import example.scannable_scoped.MyScope; import org.junit.Test; @@ -107,6 +108,7 @@ public class ComponentScanAnnotationIntegrationTests { assertThat("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected", ctx.containsBean("scannedComponent"), is(true)); + assertThat("@Bean method overrides scanned class", ctx.getBean(ConfigurableComponent.class).isFlag(), is(true)); } @Test