diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java index 422bd88bb01..624031dbed8 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java @@ -22,12 +22,12 @@ import org.springframework.context.ApplicationContext; *
Strategy interface for loading an {@link ApplicationContext application context} * for an integration test managed by the Spring TestContext Framework. * - *
The {@code SmartContextLoader} SPI supersedes the {@link ContextLoader} - * SPI introduced in Spring 2.5: a {@code SmartContextLoader} can process both - * resource locations and configuration classes. Furthermore, a {@code SmartContextLoader} - * can set active bean definition profiles in the context that it loads (see - * {@link MergedContextConfiguration#getActiveProfiles()} and - * {@link #loadContext(MergedContextConfiguration)}). + *
The {@code SmartContextLoader} SPI supersedes the {@link ContextLoader} SPI + * introduced in Spring 2.5: a {@code SmartContextLoader} can choose to process + * either resource locations or configuration classes. Furthermore, a + * {@code SmartContextLoader} can set active bean definition profiles in the + * context that it loads (see {@link MergedContextConfiguration#getActiveProfiles()} + * and {@link #loadContext(MergedContextConfiguration)}). * *
Clients of a {@code SmartContextLoader} should call
* {@link #processContextConfiguration(ContextConfigurationAttributes)
@@ -80,7 +80,6 @@ public interface SmartContextLoader extends ContextLoader {
* locations or classes property empty signals that
* this {@code SmartContextLoader} was not able to generate or detect defaults.
* @param configAttributes the context configuration attributes to process
- * @see #generatesDefaults()
*/
void processContextConfiguration(ContextConfigurationAttributes configAttributes);
diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
index 2915cf96a0e..8f95dac2c95 100644
--- a/org.springframework.test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
+++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
@@ -22,6 +22,8 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
@@ -30,7 +32,23 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
- * TODO Document DelegatingSmartContextLoader.
+ * {@code DelegatingSmartContextLoader} is an implementation of the {@link SmartContextLoader}
+ * SPI that delegates to a set of candidate SmartContextLoaders (i.e.,
+ * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}) to
+ * determine which context loader is appropriate for a given test classÕs configuration.
+ * Each candidate is given a chance to {@link #processContextConfiguration process} the
+ * {@link ContextConfigurationAttributes} for each class in the test class hierarchy that
+ * is annotated with {@link ContextConfiguration @ContextConfiguration}, and the candidate
+ * that supports the merged, processed configuration will be used to actually
+ * {@link #loadContext load} the context.
+ *
+ *
Placing an empty {@code @ContextConfiguration} annotation on + * a test class signals that default resource locations (i.e., XML configuration files) + * or default {@link Configuration configuration classes} should be detected. Furthermore, + * if a specific {@link ContextLoader} or {@link SmartContextLoader} is not explicitly + * declared via {@code @ContextConfiguration}, {@code DelegatingSmartContextLoader} will + * be used as the default loader, thus providing automatic support for either XML + * configuration files or configuration classes, but not both simultaneously. * * @author Sam Brannen * @since 3.1 @@ -43,7 +61,7 @@ public class DelegatingSmartContextLoader implements SmartContextLoader { private static final Log logger = LogFactory.getLog(DelegatingSmartContextLoader.class); private final SmartContextLoader xmlLoader = new GenericXmlContextLoader(); - private final SmartContextLoader annotationLoader = new AnnotationConfigContextLoader(); + private final SmartContextLoader annotationConfigLoader = new AnnotationConfigContextLoader(); // --- SmartContextLoader -------------------------------------------------- @@ -70,24 +88,53 @@ public class DelegatingSmartContextLoader implements SmartContextLoader { } /** - * TODO Document processContextConfiguration() implementation. + * Delegates to candidate {@code SmartContextLoaders} to process the supplied + * {@link ContextConfigurationAttributes}. + * + *
Delegation is based on explicit knowledge of the implementations of + * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}. + * Specifically, the delegation algorithm is as follows: + * + *
null, or if the supplied configuration attributes include both
+ * resource locations and configuration classes
+ * @throws IllegalStateException if {@code GenericXmlContextLoader} detects default
+ * configuration classes; if {@code AnnotationConfigContextLoader} detects default
+ * resource locations; if neither candidate loader detects defaults for the supplied
+ * context configuration; or if both candidate loaders detect defaults for the
+ * supplied context configuration
*/
public void processContextConfiguration(final ContextConfigurationAttributes configAttributes) {
- if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
- throw new IllegalStateException(String.format(
- "Cannot process locations AND configuration classes for context "
- + "configuration %s; configure one or the other, but not both.", configAttributes));
- }
+ Assert.notNull(configAttributes, "configAttributes must not be null");
+ Assert.isTrue(configAttributes.hasLocations() && configAttributes.hasClasses(), String.format(
+ "Cannot process locations AND configuration classes for context "
+ + "configuration %s; configure one or the other, but not both.", configAttributes));
// If the original locations or classes were not empty, there's no
- // need to bother with default detection checks; just let the respective
- // loader process the configuration.
+ // need to bother with default detection checks; just let the
+ // appropriate loader process the configuration.
if (configAttributes.hasLocations()) {
delegateProcessing(xmlLoader, configAttributes);
}
else if (configAttributes.hasClasses()) {
- delegateProcessing(annotationLoader, configAttributes);
+ delegateProcessing(annotationConfigLoader, configAttributes);
}
else {
// Else attempt to detect defaults...
@@ -109,28 +156,28 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
name(xmlLoader), configAttributes));
}
- // Now let the annotation loader process the configuration.
- delegateProcessing(annotationLoader, configAttributes);
+ // Now let the annotation config loader process the configuration.
+ delegateProcessing(annotationConfigLoader, configAttributes);
if (configAttributes.hasClasses()) {
if (logger.isInfoEnabled()) {
logger.info(String.format(
"%s detected default configuration classes for context configuration %s.",
- name(annotationLoader), configAttributes));
+ name(annotationConfigLoader), configAttributes));
}
}
if (!xmlLoaderDetectedDefaults && configAttributes.hasLocations()) {
throw new IllegalStateException(String.format(
"%s should NOT have detected default locations for context configuration %s.",
- name(annotationLoader), configAttributes));
+ name(annotationConfigLoader), configAttributes));
}
// If neither loader detected defaults, throw an exception.
if (!configAttributes.hasResources()) {
throw new IllegalStateException(String.format(
"Neither %s nor %s was able to detect defaults for context configuration %s.", name(xmlLoader),
- name(annotationLoader), configAttributes));
+ name(annotationConfigLoader), configAttributes));
}
if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
@@ -145,16 +192,35 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
}
/**
- * TODO Document loadContext(MergedContextConfiguration) implementation.
+ * Delegates to an appropriate candidate {@code SmartContextLoader} to load
+ * an {@link ApplicationContext}.
+ *
+ * Delegation is based on explicit knowledge of the implementations of + * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}. + * Specifically, the delegation algorithm is as follows: + * + *
null
+ * @throws IllegalStateException if neither candidate loader is capable of loading an
+ * {@code ApplicationContext} from the supplied merged context configuration
*/
public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
Assert.notNull(mergedConfig, "mergedConfig must not be null");
- List