diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 9125716580d..d6eeabf7e90 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -27,7 +27,7 @@ import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.core.OrderComparator; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.io.UrlResource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -48,12 +48,13 @@ import org.springframework.util.StringUtils; * * @author Arjen Poutsma * @author Juergen Hoeller + * @author Sam Brannen * @since 3.2 */ public abstract class SpringFactoriesLoader { /** The location to look for the factories. Can be present in multiple JAR files. */ - private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); @@ -61,7 +62,7 @@ public abstract class SpringFactoriesLoader { /** * Load the factory implementations of the given type from the default location, * using the given class loader. - *
The returned factories are ordered in accordance with the {@link OrderComparator}. + *
The returned factories are ordered in accordance with the {@link AnnotationAwareOrderComparator}. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default) */ @@ -79,7 +80,7 @@ public abstract class SpringFactoriesLoader { for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } - OrderComparator.sort(result); + AnnotationAwareOrderComparator.sort(result); return result; } diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java index b3dbceb10c4..6e1386fe33a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java @@ -79,6 +79,7 @@ public class MergedContextConfiguration implements Serializable { private final String[] propertySourceProperties; private final ContextLoader contextLoader; private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate; + private final MergedContextConfiguration parent; @@ -185,6 +186,18 @@ public class MergedContextConfiguration implements Serializable { cacheAwareContextLoaderDelegate, parent); } + /** + * Create a new {@code MergedContextConfiguration} instance by copying + * all fields from the supplied {@code MergedContextConfiguration}. + * @since 4.1 + */ + public MergedContextConfiguration(MergedContextConfiguration mergedConfig) { + this(mergedConfig.testClass, mergedConfig.locations, mergedConfig.classes, + mergedConfig.contextInitializerClasses, mergedConfig.activeProfiles, mergedConfig.propertySourceLocations, + mergedConfig.propertySourceProperties, mergedConfig.contextLoader, + mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent); + } + /** * Create a new {@code MergedContextConfiguration} instance for the * supplied parameters. diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java index 72c1be0acaa..a61bb7cf009 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java @@ -63,8 +63,16 @@ public interface TestContextBootstrapper { * for the test class in the {@link BootstrapContext} associated with this bootstrapper. *
If {@link TestExecutionListeners @TestExecutionListeners} is not * present on the test class in the {@code BootstrapContext}, - * default listeners should be returned. Concrete implementations - * are free to determine what comprises the set of default listeners. + * default listeners should be returned. Furthermore, default + * listeners must be sorted using + * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator + * AnnotationAwareOrderComparator}. + *
Concrete implementations are free to determine what comprises the + * set of default listeners. However, by default, the Spring TestContext + * Framework will use the + * {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader} + * mechanism to look up all {@code TestExecutionListener} class names + * configured in all {@code META-INF/spring.factories} files on the classpath. *
The {@link TestExecutionListeners#inheritListeners() inheritListeners} * flag of {@link TestExecutionListeners @TestExecutionListeners} must be * taken into consideration. Specifically, if the {@code inheritListeners} diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java index 74b9ee5f6c4..8eb58591d5a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java @@ -23,7 +23,13 @@ package org.springframework.test.context; *
Concrete implementations must provide a {@code public} no-args constructor, * so that listeners can be instantiated transparently by tools and configuration * mechanisms. - *
Spring provides the following out-of-the-box implementations: + *
Implementations may optionally declare the position in which they should + * be ordered among the chain of default listeners via the + * {@link org.springframework.core.Ordered Order} interface or + * {@link org.springframework.core.annotation.Order @Order} annotation. See + * {@link TestContextBootstrapper#getTestExecutionListeners()} for details. + *
Spring provides the following out-of-the-box implementations (all of + * which are annotated with {@code @Order}): *
Concrete subclasses typically will only need to provide implementations for - * the following {@code abstract} methods: + * the following methods: *
The default implementation looks up all + * {@code org.springframework.test.context.TestExecutionListener} entries + * configured in all {@code META-INF/spring.factories} files on the classpath. + *
This method is invoked by {@link #getDefaultTestExecutionListenerClasses()}.
+ * @return an unmodifiable list of names of default {@code TestExecutionListener}
+ * classes
+ * @see SpringFactoriesLoader#loadFactoryNames
+ */
+ protected List This method is invoked by {@link #getDefaultTestExecutionListenerClasses()}.
- * @return an unmodifiable list of names of default {@code
- * TestExecutionListener} classes
- */
- protected abstract List Concrete subclasses typically will only need to instantiate
- * {@link MergedContextConfiguration} (or a specialized subclass thereof)
- * from the provided values; further processing and merging of values is likely
- * unnecessary.
- * @param testClass the test class for which the {@code MergedContextConfiguration}
- * should be built (must not be {@code null})
- * @param locations the merged resource locations
- * @param classes the merged annotated classes
- * @param initializerClasses the merged context initializer classes
- * @param activeProfiles the merged active bean definition profiles
- * @param propertySourceLocations the merged {@code PropertySource} locations
- * @param propertySourceProperties the merged {@code PropertySource} properties
- * @param contextLoader the resolved {@code ContextLoader}
- * @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate
- * to be provided to the instantiated {@code MergedContextConfiguration}
- * @param parentConfig the merged context configuration for the parent application
- * context in a context hierarchy, or {@code null} if there is no parent
- * @return the fully initialized {@code MergedContextConfiguration}
+ * Process the supplied, newly instantiated {@link MergedContextConfiguration} instance.
+ * The returned {@link MergedContextConfiguration} instance may be a wrapper
+ * around or a replacement for the original.
+ * The default implementation simply returns the supplied instance unmodified.
+ * Concrete subclasses may choose to return a specialized subclass of
+ * {@link MergedContextConfiguration} based on properties in the supplied instance.
+ * @param mergedConfig the {@code MergedContextConfiguration} to process;
+ * never {@code null}
+ * @return a fully initialized {@code MergedContextConfiguration}; never
+ * {@code null}
*/
- protected abstract MergedContextConfiguration buildMergedContextConfiguration(
- Class> testClass,
- String[] locations,
- Class>[] classes,
- Set Uses {@link DelegatingSmartContextLoader} as the default {@link ContextLoader}.
*
* @author Sam Brannen
* @since 4.1
*/
public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrapper {
- private static final List Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
*/
public static final String REINJECT_DEPENDENCIES_ATTRIBUTE = Conventions.getQualifiedAttributeName(
- DependencyInjectionTestExecutionListener.class, "reinjectDependencies");
+ DependencyInjectionTestExecutionListener.class, "reinjectDependencies");
private static final Log logger = LogFactory.getLog(DependencyInjectionTestExecutionListener.class);
+ /**
+ * Returns {@code 2000}.
+ */
+ @Override
+ public final int getOrder() {
+ return 2000;
+ }
+
/**
* Performs dependency injection on the
* {@link TestContext#getTestInstance() test instance} of the supplied
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java
index 222b5ae3e56..dfbb48f11a7 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -48,20 +48,11 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
/**
- * Marks the {@linkplain ApplicationContext application context} of the supplied
- * {@linkplain TestContext test context} as
- * {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
- * and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
- * in the test context to {@code true}.
- * @param testContext the test context whose application context should
- * marked as dirty
- * @param hierarchyMode the context cache clearing mode to be applied if the
- * context is part of a hierarchy; may be {@code null}
- * @since 3.2.2
+ * Returns {@code 3000}.
*/
- protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
- testContext.markApplicationContextDirty(hierarchyMode);
- testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
+ @Override
+ public final int getOrder() {
+ return 3000;
}
/**
@@ -132,4 +123,21 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
}
}
+ /**
+ * Marks the {@linkplain ApplicationContext application context} of the supplied
+ * {@linkplain TestContext test context} as
+ * {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
+ * and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
+ * in the test context to {@code true}.
+ * @param testContext the test context whose application context should
+ * marked as dirty
+ * @param hierarchyMode the context cache clearing mode to be applied if the
+ * context is part of a hierarchy; may be {@code null}
+ * @since 3.2.2
+ */
+ protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
+ testContext.markApplicationContextDirty(hierarchyMode);
+ testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
index 64757391336..508394dc73c 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
@@ -144,6 +144,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
private TransactionConfigurationAttributes configurationAttributes;
+ /**
+ * Returns {@code 4000}.
+ */
+ @Override
+ public final int getOrder() {
+ return 4000;
+ }
+
/**
* If the test method of the supplied {@linkplain TestContext test context}
* is configured to run within a transaction, this method will run
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 a5574d9fe38..b758038247e 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
@@ -87,6 +87,14 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
private static final Log logger = LogFactory.getLog(ServletTestExecutionListener.class);
+ /**
+ * Returns {@code 1000}.
+ */
+ @Override
+ public final int getOrder() {
+ return 1000;
+ }
+
/**
* Sets up thread-local state during the test instance preparation
* callback phase via Spring Web's {@link RequestContextHolder}, but only if
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
index 4164c579e58..fb577b6aa1c 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
@@ -84,8 +84,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, resourceBasePath,
- contextLoader,
- null, null);
+ contextLoader, null, null);
}
/**
@@ -122,6 +121,19 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
contextLoader, cacheAwareContextLoaderDelegate, parent);
}
+ /**
+ * Create a new {@code WebMergedContextConfiguration} instance by copying
+ * all properties from the supplied {@code MergedContextConfiguration}.
+ * If an empty value is supplied for the {@code resourceBasePath}
+ * an empty string will be used.
+ * @param resourceBasePath the resource path to the root directory of the web application
+ * @since 4.1
+ */
+ public WebMergedContextConfiguration(MergedContextConfiguration mergedConfig, String resourceBasePath) {
+ super(mergedConfig);
+ this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
+ }
+
/**
* Create a new {@code WebMergedContextConfiguration} instance for the
* supplied parameters.
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
index 625739260b5..b60e83af4f7 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
@@ -16,34 +16,21 @@
package org.springframework.test.context.web;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.test.context.CacheAwareContextLoaderDelegate;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextBootstrapper;
-import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
/**
* Web-specific implementation of the {@link TestContextBootstrapper} SPI.
*
*
- *
+ *
- *
- *
- *
- */
- protected List
- *
*
* @author Sam Brannen
@@ -51,18 +38,6 @@ import org.springframework.test.context.support.DefaultTestContextBootstrapper;
*/
public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
- /**
- * Prepends {@link org.springframework.test.context.web.ServletTestExecutionListener}
- * to the list of default {@link TestExecutionListener TestExecutionListeners}
- * supported by the superclass and returns an unmodifiable, updated list.
- */
- @Override
- protected List