Skip to content

Commit cbd1342

Browse files
committed
Polish ContextCustomizer support in the TCF
Issue: SPR-13998
1 parent 87a3a5c commit cbd1342

13 files changed

+164
-184
lines changed

spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,34 @@
1616

1717
package org.springframework.test.context;
1818

19-
import org.springframework.context.ApplicationContext;
2019
import org.springframework.context.ConfigurableApplicationContext;
2120

2221
/**
23-
* Strategy interface for customizing {@link ApplicationContext application contexts} that
24-
* are created and managed by the Spring TestContext Framework.
22+
* Strategy interface for customizing {@link ConfigurableApplicationContext
23+
* application contexts} that are created and managed by the <em>Spring
24+
* TestContext Framework</em>.
2525
*
26-
* <p>Customizers are loaded via {@link ContextCustomizerFactory} classes registered in
27-
* {@code spring.factories}.
26+
* <p>Customizers are created by {@link ContextCustomizerFactory} implementations.
2827
*
29-
* <p>Implementations should take care to implement correct {@code equals} and
30-
* {@code hashCode} methods since customizers form part of the
31-
* {@link MergedContextConfiguration} which is used as a cache key.
28+
* <p>Implementations must implement correct {@code equals} and {@code hashCode}
29+
* methods since customizers form part of the {@link MergedContextConfiguration}
30+
* which is used as a cache key.
3231
*
3332
* @author Phillip Webb
33+
* @author Sam Brannen
3434
* @since 4.3
3535
* @see ContextCustomizerFactory
36-
* @see org.springframework.test.context.support.AbstractContextLoader
36+
* @see org.springframework.test.context.support.AbstractContextLoader#customizeContext
3737
*/
3838
public interface ContextCustomizer {
3939

4040
/**
41-
* Called <i>before</i> bean definitions are read to customize the
42-
* {@link ConfigurableApplicationContext}.
43-
* @param context the context that should be prepared
44-
* @param mergedContextConfiguration the merged context configuration
41+
* Customize the supplied {@code ConfigurableApplicationContext} <em>after</em>
42+
* bean definitions have been loaded into the context but <em>before</em> the
43+
* context has been refreshed.
44+
* @param context the context to customize
45+
* @param mergedConfig the merged context configuration
4546
*/
46-
void customizeContext(ConfigurableApplicationContext context,
47-
MergedContextConfiguration mergedContextConfiguration);
47+
void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig);
4848

4949
}

spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,35 @@
1818

1919
import java.util.List;
2020

21-
import org.springframework.context.ConfigurableApplicationContext;
22-
2321
/**
24-
* Factory registered in {@code spring.factories} that is used to create
25-
* {@link ContextCustomizer ContextCustomizers}. Factories are called after
26-
* {@link ContextLoader ContextLoaders} have been triggered but before the
22+
* Factory for creating {@link ContextCustomizer ContextCustomizers}.
23+
*
24+
* <p>Factories are invoked after {@link ContextLoader ContextLoaders} have
25+
* processed context configuration attributes but before the
2726
* {@link MergedContextConfiguration} is created.
2827
*
28+
* <p>By default, the Spring TestContext Framework will use the
29+
* {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
30+
* mechanism for loading factories configured in all {@code META-INF/spring.factories}
31+
* files on the classpath.
32+
*
2933
* @author Phillip Webb
34+
* @author Sam Brannen
3035
* @since 4.3
3136
*/
3237
public interface ContextCustomizerFactory {
3338

3439
/**
35-
* Get the {@link ContextCustomizer} (if any) that should be used to customize the
36-
* {@link ConfigurableApplicationContext} when it is created.
40+
* Create a {@link ContextCustomizer} that should be used to customize a
41+
* {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
42+
* before it is refreshed.
3743
* @param testClass the test class
38-
* @param configurationAttributes he list of context configuration attributes for the
39-
* test class, ordered <em>bottom-up</em> (i.e., as if we were traversing up the class
40-
* hierarchy); never {@code null} or empty.
41-
* @return a {@link ContextCustomizer} or {@code null}
44+
* @param configAttributes the list of context configuration attributes for
45+
* the test class, ordered <em>bottom-up</em> (i.e., as if we were traversing
46+
* up the class hierarchy); never {@code null} or empty
47+
* @return a {@link ContextCustomizer} or {@code null} if no customizer should
48+
* be used
4249
*/
43-
ContextCustomizer getContextCustomizer(Class<?> testClass,
44-
List<ContextConfigurationAttributes> configurationAttributes);
50+
ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes);
4551

4652
}

spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
* that was loaded using properties of this {@code MergedContextConfiguration}.
5656
*
5757
* @author Sam Brannen
58+
* @author Phillip Webb
5859
* @since 3.1
5960
* @see ContextConfiguration
6061
* @see ContextHierarchy
@@ -74,6 +75,8 @@ public class MergedContextConfiguration implements Serializable {
7475
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES =
7576
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
7677

78+
private static final Set<ContextCustomizer> EMPTY_CONTEXT_CUSTOMIZERS = Collections.<ContextCustomizer> emptySet();
79+
7780

7881
private final Class<?> testClass;
7982

@@ -113,6 +116,11 @@ private static Set<Class<? extends ApplicationContextInitializer<? extends Confi
113116
Collections.unmodifiableSet(contextInitializerClasses) : EMPTY_INITIALIZER_CLASSES);
114117
}
115118

119+
private static Set<ContextCustomizer> processContextCustomizers(Set<ContextCustomizer> contextCustomizers) {
120+
return (contextCustomizers != null ?
121+
Collections.unmodifiableSet(contextCustomizers) : EMPTY_CONTEXT_CUSTOMIZERS);
122+
}
123+
116124
private static String[] processActiveProfiles(String[] activeProfiles) {
117125
if (activeProfiles == null) {
118126
return EMPTY_STRING_ARRAY;
@@ -247,8 +255,8 @@ Collections.<ContextCustomizer> emptySet(), contextLoader,
247255
* <p>If a {@code null} value is supplied for {@code locations},
248256
* {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
249257
* or {@code propertySourceProperties} an empty array will be stored instead.
250-
* If a {@code null} value is supplied for the
251-
* {@code contextInitializerClasses} an empty set will be stored instead.
258+
* If a {@code null} value is supplied for {@code contextInitializerClasses}
259+
* or {@code contextCustomizers}, an empty set will be stored instead.
252260
* Furthermore, active profiles will be sorted, and duplicate profiles
253261
* will be removed.
254262
* @param testClass the test class for which the configuration was merged
@@ -258,11 +266,12 @@ Collections.<ContextCustomizer> emptySet(), contextLoader,
258266
* @param activeProfiles the merged active bean definition profiles
259267
* @param propertySourceLocations the merged {@code PropertySource} locations
260268
* @param propertySourceProperties the merged {@code PropertySource} properties
269+
* @param contextCustomizers the context customizers
261270
* @param contextLoader the resolved {@code ContextLoader}
262271
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
263272
* delegate with which to retrieve the parent context
264273
* @param parent the parent configuration or {@code null} if there is no parent
265-
* @since 4.2
274+
* @since 4.3
266275
*/
267276
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
268277
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
@@ -277,7 +286,7 @@ public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<
277286
this.activeProfiles = processActiveProfiles(activeProfiles);
278287
this.propertySourceLocations = processStrings(propertySourceLocations);
279288
this.propertySourceProperties = processStrings(propertySourceProperties);
280-
this.contextCustomizers = Collections.unmodifiableSet(contextCustomizers);
289+
this.contextCustomizers = processContextCustomizers(contextCustomizers);
281290
this.contextLoader = contextLoader;
282291
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
283292
this.parent = parent;
@@ -390,7 +399,7 @@ public String[] getPropertySourceProperties() {
390399
* when the application context is loaded.
391400
*/
392401
public Set<ContextCustomizer> getContextCustomizers() {
393-
return contextCustomizers;
402+
return this.contextCustomizers;
394403
}
395404

396405
/**
@@ -515,6 +524,7 @@ public int hashCode() {
515524
* {@linkplain #getActiveProfiles() active profiles},
516525
* {@linkplain #getPropertySourceLocations() property source locations},
517526
* {@linkplain #getPropertySourceProperties() property source properties},
527+
* {@linkplain #getContextCustomizers() context customizers},
518528
* the name of the {@link #getContextLoader() ContextLoader}, and the
519529
* {@linkplain #getParent() parent configuration}.
520530
*/
@@ -528,6 +538,7 @@ public String toString() {
528538
.append("activeProfiles", ObjectUtils.nullSafeToString(this.activeProfiles))
529539
.append("propertySourceLocations", ObjectUtils.nullSafeToString(this.propertySourceLocations))
530540
.append("propertySourceProperties", ObjectUtils.nullSafeToString(this.propertySourceProperties))
541+
.append("contextCustomizers", this.contextCustomizers)
531542
.append("contextLoader", nullSafeToString(this.contextLoader))
532543
.append("parent", this.parent)
533544
.toString();

spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.springframework.context.ApplicationContextException;
2929
import org.springframework.context.ApplicationContextInitializer;
3030
import org.springframework.context.ConfigurableApplicationContext;
31-
import org.springframework.context.support.GenericApplicationContext;
3231
import org.springframework.core.GenericTypeResolver;
3332
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3433
import org.springframework.core.env.PropertySource;
@@ -58,10 +57,13 @@
5857
*
5958
* @author Sam Brannen
6059
* @author Juergen Hoeller
60+
* @author Phillip Webb
6161
* @since 2.5
6262
* @see #generateDefaultLocations
6363
* @see #getResourceSuffixes
6464
* @see #modifyLocations
65+
* @see #prepareContext
66+
* @see #customizeContext
6567
*/
6668
public abstract class AbstractContextLoader implements SmartContextLoader {
6769

@@ -109,8 +111,6 @@ public void processContextConfiguration(ContextConfigurationAttributes configAtt
109111
* {@linkplain MergedContextConfiguration#getPropertySourceProperties()
110112
* inlined properties} from the supplied {@code MergedContextConfiguration}
111113
* to the {@code Environment} of the context.</li>
112-
* <li>Calls any {@link MergedContextConfiguration#getContextCustomizers()
113-
* ContextCustomizers} that are part of the {@link MergedContextConfiguration}.</li>
114114
* <li>Determines what (if any) context initializer classes have been supplied
115115
* via the {@code MergedContextConfiguration} and instantiates and
116116
* {@linkplain ApplicationContextInitializer#initialize invokes} each with the
@@ -173,18 +173,16 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext
173173

174174
/**
175175
* Customize the {@link ConfigurableApplicationContext} created by this
176-
* {@code ContextLoader} <i>after</i> bean definitions have been
177-
* loaded into the context but <i>before</i> the context is refreshed.
178-
*
179-
* <p>The default implementation triggers all the
180-
* {@link MergedContextConfiguration#getContextCustomizers() context customizers} that
181-
* have been registered with the {@code mergedConfig}.
182-
*
176+
* {@code ContextLoader} <em>after</em> bean definitions have been loaded
177+
* into the context but <em>before</em> the context has been refreshed.
178+
* <p>The default implementation delegates to all
179+
* {@link MergedContextConfiguration#getContextCustomizers context customizers}
180+
* that have been registered with the supplied {@code mergedConfig}.
183181
* @param context the newly created application context
184182
* @param mergedConfig the merged context configuration
185183
* @since 4.3
186184
*/
187-
protected void customizeContext(GenericApplicationContext context, MergedContextConfiguration mergedConfig) {
185+
protected void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
188186
for (ContextCustomizer contextCustomizer : mergedConfig.getContextCustomizers()) {
189187
contextCustomizer.customizeContext(context, mergedConfig);
190188
}

spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) t
253253
}
254254

255255
// If neither of the candidates supports the mergedConfig based on resources but
256-
// ACIs were declared, then delegate to the annotation config loader.
256+
// ACIs or customizers were declared, then delegate to the annotation config
257+
// loader.
257258
if (!mergedConfig.getContextInitializerClasses().isEmpty() || !mergedConfig.getContextCustomizers().isEmpty()) {
258259
return delegateLoading(getAnnotationConfigLoader(), mergedConfig);
259260
}

spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.test.context.support;
1818

19-
2019
import org.apache.commons.logging.Log;
2120
import org.apache.commons.logging.LogFactory;
2221

@@ -53,6 +52,7 @@
5352
*
5453
* @author Sam Brannen
5554
* @author Juergen Hoeller
55+
* @author Phillip Webb
5656
* @since 2.5
5757
* @see #loadContext(MergedContextConfiguration)
5858
* @see #loadContext(String...)
@@ -92,6 +92,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
9292
* annotation configuration processors.</li>
9393
* <li>Calls {@link #customizeContext(GenericApplicationContext)} to allow for customizing the context
9494
* before it is refreshed.</li>
95+
* <li>Calls {@link #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)} to
96+
* allow for customizing the context before it is refreshed.</li>
9597
* <li>{@link ConfigurableApplicationContext#refresh Refreshes} the
9698
* context and registers a JVM shutdown hook for it.</li>
9799
* </ul>
@@ -206,6 +208,7 @@ public final ConfigurableApplicationContext loadContext(String... locations) thr
206208
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
207209
* @see GenericApplicationContext#setResourceLoader
208210
* @see GenericApplicationContext#setId
211+
* @see #prepareContext(ConfigurableApplicationContext, MergedContextConfiguration)
209212
* @since 2.5
210213
*/
211214
protected void prepareContext(GenericApplicationContext context) {
@@ -279,6 +282,7 @@ protected void loadBeanDefinitions(GenericApplicationContext context, MergedCont
279282
* @param context the newly created application context
280283
* @see #loadContext(MergedContextConfiguration)
281284
* @see #loadContext(String...)
285+
* @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
282286
* @since 2.5
283287
*/
284288
protected void customizeContext(GenericApplicationContext context) {

spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,16 @@ private MergedContextConfiguration buildMergedContextConfiguration(Class<?> test
410410
return processMergedContextConfiguration(mergedConfig);
411411
}
412412

413+
/**
414+
* @since 4.3
415+
*/
413416
private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
414-
List<ContextConfigurationAttributes> configurationAttributes) {
415-
List<ContextCustomizerFactory> factories = geContextCustomizerFactories();
417+
List<ContextConfigurationAttributes> configAttributes) {
418+
419+
List<ContextCustomizerFactory> factories = getContextCustomizerFactories();
416420
Set<ContextCustomizer> customizers = new LinkedHashSet<ContextCustomizer>(factories.size());
417421
for (ContextCustomizerFactory factory : factories) {
418-
ContextCustomizer customizer = factory.getContextCustomizer(testClass, configurationAttributes);
422+
ContextCustomizer customizer = factory.createContextCustomizer(testClass, configAttributes);
419423
if (customizer != null) {
420424
customizers.add(customizer);
421425
}
@@ -424,13 +428,20 @@ private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
424428
}
425429

426430
/**
427-
* Get the default {@link ContextCustomizerFactory} instances for this bootstrapper.
431+
* Get the {@link ContextCustomizerFactory} instances for this bootstrapper.
432+
* <p>The default implementation uses the {@link SpringFactoriesLoader} mechanism
433+
* for loading factories configured in all {@code META-INF/spring.factories}
434+
* files on the classpath.
435+
* @since 4.3
436+
* @see SpringFactoriesLoader#loadFactories
428437
*/
429-
protected List<ContextCustomizerFactory> geContextCustomizerFactories() {
430-
return SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class,
431-
getClass().getClassLoader());
438+
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
439+
return SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, getClass().getClassLoader());
432440
}
433441

442+
/**
443+
* @since 4.3
444+
*/
434445
private boolean areAllEmpty(Collection<?>... collections) {
435446
for (Collection<?> collection : collections) {
436447
if (!collection.isEmpty()) {

spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
5353
* {@link #loadBeanDefinitions}.
5454
*
5555
* @author Sam Brannen
56+
* @author Phillip Webb
5657
* @since 3.2
5758
* @see #loadContext(MergedContextConfiguration)
5859
* @see #loadContext(String...)
@@ -256,10 +257,14 @@ protected abstract void loadBeanDefinitions(GenericWebApplicationContext context
256257
* loader <i>after</i> bean definitions have been loaded into the context but
257258
* <i>before</i> the context is refreshed.
258259
*
260+
* <p>The default implementation simply delegates to
261+
* {@link AbstractContextLoader#customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)}.
262+
*
259263
* @param context the newly created web application context
260264
* @param webMergedConfig the merged context configuration to use to load the
261265
* web application context
262266
* @see #loadContext(MergedContextConfiguration)
267+
* @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
263268
*/
264269
protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
265270
super.customizeContext(context, webMergedConfig);

0 commit comments

Comments
 (0)