diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
index 5e2e235707..425878a543 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
@@ -19,6 +19,8 @@ package org.springframework.test.context;
import static org.springframework.beans.BeanUtils.*;
import static org.springframework.core.annotation.AnnotationUtils.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -27,10 +29,10 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.test.context.web.WebMergedContextConfiguration;
+import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -57,6 +59,9 @@ abstract class ContextLoaderUtils {
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.DelegatingSmartContextLoader";
private static final String DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.WebDelegatingSmartContextLoader";
+ private static final String WEB_APP_CONFIGURATION_CLASS_NAME = "org.springframework.test.context.web.WebAppConfiguration";
+ private static final String WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME = "org.springframework.test.context.web.WebMergedContextConfiguration";
+
private ContextLoaderUtils() {
/* no-op */
@@ -69,7 +74,8 @@ abstract class ContextLoaderUtils {
*
*
If the supplied defaultContextLoaderClassName is
* {@code null} or empty, depending on the absence or presence
- * of @{@link WebAppConfiguration} either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
+ * of {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
+ * either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
* or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the
* default context loader class name. For details on the class resolution
* process, see {@link #resolveContextLoaderClass()}.
@@ -91,7 +97,9 @@ abstract class ContextLoaderUtils {
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
if (!StringUtils.hasText(defaultContextLoaderClassName)) {
- defaultContextLoaderClassName = testClass.isAnnotationPresent(WebAppConfiguration.class) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME
+ Class extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
+ defaultContextLoaderClassName = webAppConfigClass != null
+ && testClass.isAnnotationPresent(webAppConfigClass) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME
: DEFAULT_CONTEXT_LOADER_CLASS_NAME;
}
@@ -385,16 +393,82 @@ abstract class ContextLoaderUtils {
Set>> initializerClasses = resolveInitializerClasses(configAttributesList);
String[] activeProfiles = resolveActiveProfiles(testClass);
- if (testClass.isAnnotationPresent(WebAppConfiguration.class)) {
- WebAppConfiguration webAppConfig = testClass.getAnnotation(WebAppConfiguration.class);
- String resourceBasePath = webAppConfig.value();
- return new WebMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- resourceBasePath, contextLoader);
+ MergedContextConfiguration mergedConfig = buildWebMergedContextConfiguration(testClass, locations, classes,
+ initializerClasses, activeProfiles, contextLoader);
+
+ if (mergedConfig == null) {
+ mergedConfig = new MergedContextConfiguration(testClass, locations, classes, initializerClasses,
+ activeProfiles, contextLoader);
}
- // else
- return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- contextLoader);
+ return mergedConfig;
+ }
+
+ /**
+ * Load the {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
+ * class using reflection in order to avoid package cycles.
+ *
+ * @return the {@code @WebAppConfiguration} class or null if it
+ * cannot be loaded
+ */
+ @SuppressWarnings("unchecked")
+ private static Class extends Annotation> loadWebAppConfigurationClass() {
+ Class extends Annotation> webAppConfigClass = null;
+ try {
+ webAppConfigClass = (Class extends Annotation>) ClassUtils.forName(WEB_APP_CONFIGURATION_CLASS_NAME,
+ ContextLoaderUtils.class.getClassLoader());
+ }
+ catch (Throwable t) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Could not load @WebAppConfiguration class [" + WEB_APP_CONFIGURATION_CLASS_NAME + "].", t);
+ }
+ }
+ return webAppConfigClass;
+ }
+
+ /**
+ * Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration
+ * WebMergedContextConfiguration} from the supplied arguments using reflection
+ * in order to avoid package cycles.
+ *
+ * @return the {@code WebMergedContextConfiguration} or null if
+ * it could not be built
+ */
+ @SuppressWarnings("unchecked")
+ private static MergedContextConfiguration buildWebMergedContextConfiguration(
+ Class> testClass,
+ String[] locations,
+ Class>[] classes,
+ Set>> initializerClasses,
+ String[] activeProfiles, ContextLoader contextLoader) {
+
+ Class extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
+
+ if (webAppConfigClass != null && testClass.isAnnotationPresent(webAppConfigClass)) {
+ Annotation annotation = testClass.getAnnotation(webAppConfigClass);
+ String resourceBasePath = (String) AnnotationUtils.getValue(annotation);
+
+ try {
+ Class extends MergedContextConfiguration> webMergedConfigClass = (Class extends MergedContextConfiguration>) ClassUtils.forName(
+ WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME, ContextLoaderUtils.class.getClassLoader());
+
+ Constructor extends MergedContextConfiguration> constructor = ClassUtils.getConstructorIfAvailable(
+ webMergedConfigClass, Class.class, String[].class, Class[].class, Set.class, String[].class,
+ String.class, ContextLoader.class);
+
+ if (constructor != null) {
+ return instantiateClass(constructor, testClass, locations, classes, initializerClasses,
+ activeProfiles, resourceBasePath, contextLoader);
+ }
+ }
+ catch (Throwable t) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Could not instantiate [" + WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME + "].", t);
+ }
+ }
+ }
+
+ return null;
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
index 969234061e..1e999d7e13 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
@@ -48,7 +48,7 @@ public class ServletTestExecutionListener implements TestExecutionListener {
* The default implementation is empty. Can be overridden by
* subclasses as necessary.
*
- * @see org.springframework.test.context.TestExecutionListener#beforeTestClass(TestContext)
+ * @see TestExecutionListener#beforeTestClass(TestContext)
*/
public void beforeTestClass(TestContext testContext) throws Exception {
/* no-op */
@@ -57,7 +57,7 @@ public class ServletTestExecutionListener implements TestExecutionListener {
/**
* TODO [SPR-9864] Document overridden prepareTestInstance().
*
- * @see org.springframework.test.context.TestExecutionListener#prepareTestInstance(TestContext)
+ * @see TestExecutionListener#prepareTestInstance(TestContext)
*/
public void prepareTestInstance(TestContext testContext) throws Exception {
setUpRequestContextIfNecessary(testContext);
@@ -66,7 +66,7 @@ public class ServletTestExecutionListener implements TestExecutionListener {
/**
* TODO [SPR-9864] Document overridden beforeTestMethod().
*
- * @see org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)
+ * @see TestExecutionListener#beforeTestMethod(TestContext)
*/
public void beforeTestMethod(TestContext testContext) throws Exception {
setUpRequestContextIfNecessary(testContext);
@@ -75,7 +75,7 @@ public class ServletTestExecutionListener implements TestExecutionListener {
/**
* TODO [SPR-9864] Document overridden afterTestMethod().
*
- * @see org.springframework.test.context.TestExecutionListener#afterTestMethod(TestContext)
+ * @see TestExecutionListener#afterTestMethod(TestContext)
*/
public void afterTestMethod(TestContext testContext) throws Exception {
if (logger.isDebugEnabled()) {
@@ -88,7 +88,7 @@ public class ServletTestExecutionListener implements TestExecutionListener {
* The default implementation is empty. Can be overridden by
* subclasses as necessary.
*
- * @see org.springframework.test.context.TestExecutionListener#afterTestClass(TestContext)
+ * @see TestExecutionListener#afterTestClass(TestContext)
*/
public void afterTestClass(TestContext testContext) throws Exception {
/* no-op */