Skip to content

Commit 63fac1b

Browse files
committed
Allow default CacheAwareContextLoaderDelegate configuration via system property
Prior to this commit, the default CacheAwareContextLoaderDelegate could be configured by extending AbstractTestContextBootstrapper and overriding getCacheAwareContextLoaderDelegate(); however, this required that the user configure the custom TestContextBootstrapper via @BootstrapWith. This commit introduces a new "spring.test.context.default.CacheAwareContextLoaderDelegate" property that can be configured via a JVM system property or via the SpringProperties mechanism. BootstrapUtils uses this new property to load the default CacheAwareContextLoaderDelegate. If the property is not defined, BootstrapUtils will fall back to creating a DefaultCacheAwareContextLoaderDelegate as it did previously. This allows third parties to configure the default CacheAwareContextLoaderDelegate transparently for the user -- for example, to intercept context loading in order to load the context in a different manner -- for example, to make use of ahead of time (AOT) techniques for implementing a different type of ApplicationContext at build time. Closes gh-27540
1 parent e8f6cd1 commit 63fac1b

File tree

3 files changed

+127
-10
lines changed

3 files changed

+127
-10
lines changed

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -25,9 +25,11 @@
2525
import org.apache.commons.logging.LogFactory;
2626

2727
import org.springframework.beans.BeanUtils;
28+
import org.springframework.core.SpringProperties;
2829
import org.springframework.lang.Nullable;
2930
import org.springframework.test.context.TestContextAnnotationUtils.AnnotationDescriptor;
3031
import org.springframework.util.ClassUtils;
32+
import org.springframework.util.StringUtils;
3133

3234
/**
3335
* {@code BootstrapUtils} is a collection of utility methods to assist with
@@ -65,7 +67,11 @@ abstract class BootstrapUtils {
6567
/**
6668
* Create the {@code BootstrapContext} for the specified {@linkplain Class test class}.
6769
* <p>Uses reflection to create a {@link org.springframework.test.context.support.DefaultBootstrapContext}
68-
* that uses a {@link org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate}.
70+
* that uses a default {@link CacheAwareContextLoaderDelegate} &mdash; configured
71+
* via the {@link CacheAwareContextLoaderDelegate#DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_PROPERTY_NAME}
72+
* system property or falling back to the
73+
* {@link org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate}
74+
* if the system property is not defined.
6975
* @param testClass the test class for which the bootstrap context should be created
7076
* @return a new {@code BootstrapContext}; never {@code null}
7177
*/
@@ -90,19 +96,21 @@ static BootstrapContext createBootstrapContext(Class<?> testClass) {
9096

9197
@SuppressWarnings("unchecked")
9298
private static CacheAwareContextLoaderDelegate createCacheAwareContextLoaderDelegate() {
93-
Class<? extends CacheAwareContextLoaderDelegate> clazz = null;
99+
String className = SpringProperties.getProperty(
100+
CacheAwareContextLoaderDelegate.DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_PROPERTY_NAME);
101+
className = (StringUtils.hasText(className) ? className.trim() :
102+
DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME);
94103
try {
95-
clazz = (Class<? extends CacheAwareContextLoaderDelegate>) ClassUtils.forName(
96-
DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME, BootstrapUtils.class.getClassLoader());
97-
104+
Class<? extends CacheAwareContextLoaderDelegate> clazz =
105+
(Class<? extends CacheAwareContextLoaderDelegate>) ClassUtils.forName(
106+
className, BootstrapUtils.class.getClassLoader());
98107
if (logger.isDebugEnabled()) {
99-
logger.debug(String.format("Instantiating CacheAwareContextLoaderDelegate from class [%s]",
100-
clazz.getName()));
108+
logger.debug(String.format("Instantiating CacheAwareContextLoaderDelegate from class [%s]", className));
101109
}
102110
return BeanUtils.instantiateClass(clazz, CacheAwareContextLoaderDelegate.class);
103111
}
104112
catch (Throwable ex) {
105-
throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", ex);
113+
throw new IllegalStateException("Could not create CacheAwareContextLoaderDelegate [" + className + "]", ex);
106114
}
107115
}
108116

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 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.
@@ -36,6 +36,20 @@
3636
*/
3737
public interface CacheAwareContextLoaderDelegate {
3838

39+
/**
40+
* System property used to configure the fully qualified class name of the
41+
* default {@code CacheAwareContextLoaderDelegate}.
42+
* <p>May alternatively be configured via the
43+
* {@link org.springframework.core.SpringProperties} mechanism.
44+
* <p>If this property is not defined, the
45+
* {@link org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate
46+
* DefaultCacheAwareContextLoaderDelegate} will be used as the default.
47+
* @since 5.3.11
48+
*/
49+
String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_PROPERTY_NAME =
50+
"spring.test.context.default.CacheAwareContextLoaderDelegate";
51+
52+
3953
/**
4054
* Determine if the {@linkplain ApplicationContext application context} for
4155
* the supplied {@link MergedContextConfiguration} has been loaded (i.e.,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.support;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.platform.testkit.engine.EngineTestKit;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.context.support.GenericApplicationContext;
27+
import org.springframework.core.SpringProperties;
28+
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
29+
import org.springframework.test.context.MergedContextConfiguration;
30+
import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate;
31+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
35+
36+
/**
37+
* Integration tests for configuring a custom default {@link CacheAwareContextLoaderDelegate}
38+
* via {@link SpringProperties}.
39+
*
40+
* @author sbrannen
41+
* @since 5.3.11
42+
*/
43+
class CustomDefaultCacheAwareContextLoaderDelegateTests {
44+
45+
@Test
46+
void customDefaultCacheAwareContextLoaderDelegateConfiguredViaSpringProperties() {
47+
String key = CacheAwareContextLoaderDelegate.DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_PROPERTY_NAME;
48+
49+
try {
50+
SpringProperties.setProperty(key, AotCacheAwareContextLoaderDelegate.class.getName());
51+
52+
EngineTestKit.engine("junit-jupiter")//
53+
.selectors(selectClass(TestCase.class))//
54+
.execute()//
55+
.testEvents()//
56+
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
57+
}
58+
finally {
59+
SpringProperties.setProperty(key, null);
60+
}
61+
}
62+
63+
64+
@SpringJUnitConfig
65+
static class TestCase {
66+
67+
@Test
68+
void test(@Autowired String foo) {
69+
// foo will be "bar" unless the AotCacheAwareContextLoaderDelegate is registered.
70+
assertThat(foo).isEqualTo("AOT");
71+
}
72+
73+
74+
@Configuration
75+
static class Config {
76+
77+
@Bean
78+
String foo() {
79+
return "bar";
80+
}
81+
}
82+
}
83+
84+
static class AotCacheAwareContextLoaderDelegate extends DefaultCacheAwareContextLoaderDelegate {
85+
86+
@Override
87+
protected ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration) {
88+
GenericApplicationContext applicationContext = new GenericApplicationContext();
89+
applicationContext.registerBean("foo", String.class, () -> "AOT");
90+
applicationContext.refresh();
91+
return applicationContext;
92+
}
93+
}
94+
95+
}

0 commit comments

Comments
 (0)