diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/CircularComponentScanException.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/CircularComponentScanException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f027dfff30088a4a1627cbda61a53964974caee0
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/CircularComponentScanException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2011 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 org.springframework.context.annotation;
+
+/**
+ * Exception thrown upon detection of circular {@link ComponentScan} use.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+class CircularComponentScanException extends IllegalStateException {
+
+ public CircularComponentScanException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index 9488edf117c87830ed01883f44b30fd1e1994a51..89540c332551add22ef015b35765f6cb8eccd599 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -269,7 +269,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* @return true
if the bean can be registered as-is;
* false
if it should be skipped because there is an
* existing, compatible bean definition for the specified name
- * @throws IllegalStateException if an existing, incompatible
+ * @throws ConflictingBeanDefinitionException if an existing, incompatible
* bean definition has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
@@ -284,7 +284,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
- throw new IllegalStateException("Annotation-specified bean name '" + beanName +
+ throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
index 3d73bf2d5e950e34c4a3123702b393c208ab0a53..0ec6aa0b4df159a0fe925c188779a4c85ffc80a1 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java
@@ -20,14 +20,15 @@ import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
@@ -54,15 +55,9 @@ class ComponentScanAnnotationParser {
this.registry = registry;
}
- public void parse(AnnotationMetadata annotationMetadata) {
- Map attribs = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());
- if (attribs == null) {
- // @ComponentScan annotation is not present -> do nothing
- return;
- }
-
+ public Set parse(Map componentScanAttributes) {
ClassPathBeanDefinitionScanner scanner =
- new ClassPathBeanDefinitionScanner(registry, (Boolean)attribs.get("useDefaultFilters"));
+ new ClassPathBeanDefinitionScanner(registry, (Boolean)componentScanAttributes.get("useDefaultFilters"));
Assert.notNull(this.environment, "Environment must not be null");
scanner.setEnvironment(this.environment);
@@ -71,37 +66,37 @@ class ComponentScanAnnotationParser {
scanner.setResourceLoader(this.resourceLoader);
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(
- (Class>)attribs.get("nameGenerator"), BeanNameGenerator.class));
+ (Class>)componentScanAttributes.get("nameGenerator"), BeanNameGenerator.class));
- ScopedProxyMode scopedProxyMode = (ScopedProxyMode) attribs.get("scopedProxy");
+ ScopedProxyMode scopedProxyMode = (ScopedProxyMode) componentScanAttributes.get("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(
- (Class>)attribs.get("scopeResolver"), ScopeMetadataResolver.class));
+ (Class>)componentScanAttributes.get("scopeResolver"), ScopeMetadataResolver.class));
}
- scanner.setResourcePattern((String)attribs.get("resourcePattern"));
+ scanner.setResourcePattern((String)componentScanAttributes.get("resourcePattern"));
- for (Filter filter : (Filter[])attribs.get("includeFilters")) {
+ for (Filter filter : (Filter[])componentScanAttributes.get("includeFilters")) {
scanner.addIncludeFilter(createTypeFilter(filter));
}
- for (Filter filter : (Filter[])attribs.get("excludeFilters")) {
+ for (Filter filter : (Filter[])componentScanAttributes.get("excludeFilters")) {
scanner.addExcludeFilter(createTypeFilter(filter));
}
List basePackages = new ArrayList();
- for (String pkg : (String[])attribs.get("value")) {
+ for (String pkg : (String[])componentScanAttributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
- for (String pkg : (String[])attribs.get("basePackages")) {
+ for (String pkg : (String[])componentScanAttributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
- for (Class> clazz : (Class>[])attribs.get("basePackageClasses")) {
+ for (Class> clazz : (Class>[])componentScanAttributes.get("basePackageClasses")) {
// TODO: loading user types directly here. implications on load-time
// weaving may mean we need to revert to stringified class names in
// annotation metadata
@@ -112,7 +107,7 @@ class ComponentScanAnnotationParser {
throw new IllegalStateException("At least one base package must be specified");
}
- scanner.scan(basePackages.toArray(new String[]{}));
+ return scanner.doScan(basePackages.toArray(new String[]{}));
}
private TypeFilter createTypeFilter(Filter filter) {
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
index 2e4d8dbdc07b5fd79b2889e20313eb916f857071..fd4b13e7d97ba51a03c58eb8afbc906cf9187a31 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
@@ -27,7 +27,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
@@ -44,19 +43,12 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.context.EnvironmentAware;
-import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.Conventions;
-import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
-import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
-import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
-import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
@@ -74,15 +66,8 @@ import org.springframework.util.StringUtils;
*/
public class ConfigurationClassBeanDefinitionReader {
- private static final String CONFIGURATION_CLASS_FULL = "full";
-
- private static final String CONFIGURATION_CLASS_LITE = "lite";
-
- private static final String CONFIGURATION_CLASS_ATTRIBUTE =
- Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
-
private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
-
+
private final BeanDefinitionRegistry registry;
private final SourceExtractor sourceExtractor;
@@ -93,28 +78,21 @@ public class ConfigurationClassBeanDefinitionReader {
private ResourceLoader resourceLoader;
- private Environment environment;
-
- private final ComponentScanAnnotationParser componentScanParser;
-
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
* to populate the given {@link BeanDefinitionRegistry}.
* @param problemReporter
* @param metadataReaderFactory
*/
- public ConfigurationClassBeanDefinitionReader(final BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
+ public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
- ResourceLoader resourceLoader, Environment environment) {
+ ResourceLoader resourceLoader) {
this.registry = registry;
this.sourceExtractor = sourceExtractor;
this.problemReporter = problemReporter;
this.metadataReaderFactory = metadataReaderFactory;
this.resourceLoader = resourceLoader;
- this.environment = environment;
-
- this.componentScanParser = new ComponentScanAnnotationParser(resourceLoader, environment, registry);
}
@@ -133,8 +111,6 @@ public class ConfigurationClassBeanDefinitionReader {
* class itself, all its {@link Bean} methods
*/
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
- AnnotationMetadata metadata = configClass.getMetadata();
- componentScanParser.parse(metadata);
doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
@@ -155,7 +131,7 @@ public class ConfigurationClassBeanDefinitionReader {
BeanDefinition configBeanDef = new GenericBeanDefinition();
String className = configClass.getMetadata().getClassName();
configBeanDef.setBeanClassName(className);
- if (checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
+ if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName((AbstractBeanDefinition)configBeanDef, this.registry);
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
@@ -311,59 +287,6 @@ public class ConfigurationClassBeanDefinitionReader {
}
- /**
- * Check whether the given bean definition is a candidate for a configuration class,
- * and mark it accordingly.
- * @param beanDef the bean definition to check
- * @param metadataReaderFactory the current factory in use by the caller
- * @return whether the candidate qualifies as (any kind of) configuration class
- */
- public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
- AnnotationMetadata metadata = null;
-
- // Check already loaded Class if present...
- // since we possibly can't even load the class file for this Class.
- if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
- metadata = new StandardAnnotationMetadata(((AbstractBeanDefinition) beanDef).getBeanClass());
- }
- else {
- String className = beanDef.getBeanClassName();
- if (className != null) {
- try {
- MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
- metadata = metadataReader.getAnnotationMetadata();
- }
- catch (IOException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Could not find class file for introspecting factory methods: " + className, ex);
- }
- return false;
- }
- }
- }
-
- if (metadata != null) {
- if (metadata.isAnnotated(Configuration.class.getName())) {
- beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
- return true;
- }
- else if (metadata.isAnnotated(Component.class.getName()) ||
- metadata.hasAnnotatedMethods(Bean.class.getName())) {
- beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Determine whether the given bean definition indicates a full @Configuration class.
- */
- public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
- return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
- }
-
-
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
* was created from a configuration class as opposed to any other configuration source.
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index e4bc1b4d4f6f7bc37ea62636fe527b666579b42b..b826eb9d88e06e3e59c17fa126912d56ed0fdd00 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -28,11 +28,14 @@ import java.util.Set;
import java.util.Stack;
import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
+import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
@@ -72,16 +75,24 @@ class ConfigurationClassParser {
private final Environment environment;
+ private final ResourceLoader resourceLoader;
+
+ private final ComponentScanAnnotationParser componentScanParser;
+
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
* to populate the set of configuration classes.
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
- ProblemReporter problemReporter, Environment environment) {
+ ProblemReporter problemReporter, Environment environment,
+ ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
+ this.resourceLoader = resourceLoader;
+
+ this.componentScanParser = new ComponentScanAnnotationParser(this.resourceLoader, this.environment, registry);
}
@@ -141,6 +152,25 @@ class ConfigurationClassParser {
}
protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
+ Map componentScanAttributes = metadata.getAnnotationAttributes(ComponentScan.class.getName());
+ if (componentScanAttributes != null) {
+ // the config class is annotated with @ComponentScan -> perform the scan immediately
+ Set scannedBeanDefinitions = this.componentScanParser.parse(componentScanAttributes);
+
+ // check the set of scanned definitions for any further config classes and parse recursively if necessary
+ for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
+ if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), metadataReaderFactory)) {
+ try {
+ this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
+ } catch (ConflictingBeanDefinitionException ex) {
+ throw new CircularComponentScanException(
+ "A conflicting bean definition was detected while processing @ComponentScan annotations. " +
+ "This usually indicates a circle between scanned packages.", ex);
+ }
+ }
+ }
+ }
+
List