diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializers.java b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializers.java
index 7dbf09f45d..a377ce7c09 100644
--- a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializers.java
+++ b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializers.java
@@ -32,8 +32,8 @@ import org.springframework.lang.Nullable;
*
*
If we are not running in {@linkplain AotDetector#useGeneratedArtifacts()
* AOT mode} or if a test class is not {@linkplain #isSupportedTestClass(Class)
- * supported} in AOT mode, {@link #getContextInitializer(Class)} will return
- * {@code null}.
+ * supported} in AOT mode, {@link #getContextInitializer(Class)} and
+ * {@link #getContextInitializerClass(Class)} will return {@code null}.
*
* @author Sam Brannen
* @since 6.0
@@ -42,13 +42,20 @@ public class AotTestContextInitializers {
private final Map>> contextInitializers;
+ private final Map>> contextInitializerClasses;
+
public AotTestContextInitializers() {
- this(AotTestContextInitializersFactory.getContextInitializers());
+ this(AotTestContextInitializersFactory.getContextInitializers(),
+ AotTestContextInitializersFactory.getContextInitializerClasses());
}
- AotTestContextInitializers(Map>> contextInitializers) {
+ AotTestContextInitializers(
+ Map>> contextInitializers,
+ Map>> contextInitializerClasses) {
+
this.contextInitializers = contextInitializers;
+ this.contextInitializerClasses = contextInitializerClasses;
}
@@ -67,6 +74,7 @@ public class AotTestContextInitializers {
* @return the AOT context initializer, or {@code null} if there is no AOT context
* initializer for the specified test class
* @see #isSupportedTestClass(Class)
+ * @see #getContextInitializerClass(Class)
*/
@Nullable
public ApplicationContextInitializer getContextInitializer(Class> testClass) {
@@ -75,4 +83,17 @@ public class AotTestContextInitializers {
return (supplier != null ? supplier.get() : null);
}
+ /**
+ * Get the AOT {@link ApplicationContextInitializer} {@link Class} for the
+ * specified test class.
+ * @return the AOT context initializer class, or {@code null} if there is no
+ * AOT context initializer for the specified test class
+ * @see #isSupportedTestClass(Class)
+ * @see #getContextInitializer(Class)
+ */
+ @Nullable
+ public Class> getContextInitializerClass(Class> testClass) {
+ return this.contextInitializerClasses.get(testClass.getName());
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersCodeGenerator.java b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersCodeGenerator.java
index 80944e69ec..6dfff83829 100644
--- a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersCodeGenerator.java
+++ b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersCodeGenerator.java
@@ -50,24 +50,38 @@ class AotTestContextInitializersCodeGenerator {
private static final Log logger = LogFactory.getLog(AotTestContextInitializersCodeGenerator.class);
+ // ApplicationContextInitializer extends ConfigurableApplicationContext>
private static final ParameterizedTypeName CONTEXT_INITIALIZER = ParameterizedTypeName.get(
ClassName.get(ApplicationContextInitializer.class),
WildcardTypeName.subtypeOf(ConfigurableApplicationContext.class));
+ // Supplier>
private static final ParameterizedTypeName CONTEXT_INITIALIZER_SUPPLIER = ParameterizedTypeName
.get(ClassName.get(Supplier.class), CONTEXT_INITIALIZER);
// Map>>
- private static final TypeName CONTEXT_SUPPLIER_MAP = ParameterizedTypeName
+ private static final TypeName CONTEXT_INITIALIZER_SUPPLIER_MAP = ParameterizedTypeName
.get(ClassName.get(Map.class), ClassName.get(String.class), CONTEXT_INITIALIZER_SUPPLIER);
+ // Class
+ private static final ParameterizedTypeName CONTEXT_INITIALIZER_CLASS = ParameterizedTypeName
+ .get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(
+ ParameterizedTypeName.get(ClassName.get(ApplicationContextInitializer.class),
+ WildcardTypeName.subtypeOf(Object.class))));
+
+ // Map>>
+ private static final TypeName CONTEXT_INITIALIZER_CLASS_MAP = ParameterizedTypeName
+ .get(ClassName.get(Map.class), ClassName.get(String.class), CONTEXT_INITIALIZER_CLASS);
+
private static final String GENERATED_SUFFIX = "Generated";
// TODO Consider an alternative means for specifying the name of the generated class.
// Ideally we would generate a class named: org.springframework.test.context.aot.GeneratedAotTestContextInitializers
static final String GENERATED_MAPPINGS_CLASS_NAME = AotTestContextInitializers.class.getName() + "__" + GENERATED_SUFFIX;
- static final String GENERATED_MAPPINGS_METHOD_NAME = "getContextInitializers";
+ static final String GET_CONTEXT_INITIALIZERS_METHOD_NAME = "getContextInitializers";
+
+ static final String GET_CONTEXT_INITIALIZER_CLASSES_METHOD_NAME = "getContextInitializerClasses";
private final MultiValueMap> initializerClassMappings;
@@ -92,24 +106,25 @@ class AotTestContextInitializersCodeGenerator {
this.generatedClass.getName().reflectionName()));
type.addJavadoc("Generated mappings for {@link $T}.", AotTestContextInitializers.class);
type.addModifiers(Modifier.PUBLIC);
- type.addMethod(generateMappingMethod());
+ type.addMethod(contextInitializersMappingMethod());
+ type.addMethod(contextInitializerClassesMappingMethod());
}
- private MethodSpec generateMappingMethod() {
- MethodSpec.Builder method = MethodSpec.methodBuilder(GENERATED_MAPPINGS_METHOD_NAME);
+ private MethodSpec contextInitializersMappingMethod() {
+ MethodSpec.Builder method = MethodSpec.methodBuilder(GET_CONTEXT_INITIALIZERS_METHOD_NAME);
method.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- method.returns(CONTEXT_SUPPLIER_MAP);
- method.addCode(generateMappingCode());
+ method.returns(CONTEXT_INITIALIZER_SUPPLIER_MAP);
+ method.addCode(generateContextInitializersMappingCode());
return method.build();
}
- private CodeBlock generateMappingCode() {
+ private CodeBlock generateContextInitializersMappingCode() {
CodeBlock.Builder code = CodeBlock.builder();
- code.addStatement("$T map = new $T<>()", CONTEXT_SUPPLIER_MAP, HashMap.class);
+ code.addStatement("$T map = new $T<>()", CONTEXT_INITIALIZER_SUPPLIER_MAP, HashMap.class);
this.initializerClassMappings.forEach((className, testClasses) -> {
List testClassNames = testClasses.stream().map(Class::getName).toList();
logger.debug(LogMessage.format(
- "Generating mapping from AOT context initializer [%s] to test classes %s",
+ "Generating mapping from AOT context initializer supplier [%s] to test classes %s",
className.reflectionName(), testClassNames));
testClassNames.forEach(testClassName ->
code.addStatement("map.put($S, () -> new $T())", testClassName, className));
@@ -118,4 +133,27 @@ class AotTestContextInitializersCodeGenerator {
return code.build();
}
+ private MethodSpec contextInitializerClassesMappingMethod() {
+ MethodSpec.Builder method = MethodSpec.methodBuilder(GET_CONTEXT_INITIALIZER_CLASSES_METHOD_NAME);
+ method.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ method.returns(CONTEXT_INITIALIZER_CLASS_MAP);
+ method.addCode(generateContextInitializerClassesMappingCode());
+ return method.build();
+ }
+
+ private CodeBlock generateContextInitializerClassesMappingCode() {
+ CodeBlock.Builder code = CodeBlock.builder();
+ code.addStatement("$T map = new $T<>()", CONTEXT_INITIALIZER_CLASS_MAP, HashMap.class);
+ this.initializerClassMappings.forEach((className, testClasses) -> {
+ List testClassNames = testClasses.stream().map(Class::getName).toList();
+ logger.debug(LogMessage.format(
+ "Generating mapping from AOT context initializer class [%s] to test classes %s",
+ className.reflectionName(), testClassNames));
+ testClassNames.forEach(testClassName ->
+ code.addStatement("map.put($S, $T.class)", testClassName, className));
+ });
+ code.addStatement("return map");
+ return code.build();
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersFactory.java b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersFactory.java
index e714dcf6db..8700ced840 100644
--- a/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersFactory.java
+++ b/spring-test/src/main/java/org/springframework/test/context/aot/AotTestContextInitializersFactory.java
@@ -35,6 +35,9 @@ final class AotTestContextInitializersFactory {
@Nullable
private static volatile Map>> contextInitializers;
+ @Nullable
+ private static volatile Map>> contextInitializerClasses;
+
private AotTestContextInitializersFactory() {
}
@@ -59,6 +62,20 @@ final class AotTestContextInitializersFactory {
return initializers;
}
+ static Map>> getContextInitializerClasses() {
+ Map>> initializerClasses = contextInitializerClasses;
+ if (initializerClasses == null) {
+ synchronized (AotTestContextInitializersFactory.class) {
+ initializerClasses = contextInitializerClasses;
+ if (initializerClasses == null) {
+ initializerClasses = (AotDetector.useGeneratedArtifacts() ? loadContextInitializerClassesMap() : Map.of());
+ contextInitializerClasses = initializerClasses;
+ }
+ }
+ }
+ return initializerClasses;
+ }
+
/**
* Reset the factory.
* Only for internal use.
@@ -66,13 +83,21 @@ final class AotTestContextInitializersFactory {
static void reset() {
synchronized (AotTestContextInitializersFactory.class) {
contextInitializers = null;
+ contextInitializerClasses = null;
}
}
@SuppressWarnings("unchecked")
private static Map>> loadContextInitializersMap() {
String className = AotTestContextInitializersCodeGenerator.GENERATED_MAPPINGS_CLASS_NAME;
- String methodName = AotTestContextInitializersCodeGenerator.GENERATED_MAPPINGS_METHOD_NAME;
+ String methodName = AotTestContextInitializersCodeGenerator.GET_CONTEXT_INITIALIZERS_METHOD_NAME;
+ return GeneratedMapUtils.loadMap(className, methodName);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map>> loadContextInitializerClassesMap() {
+ String className = AotTestContextInitializersCodeGenerator.GENERATED_MAPPINGS_CLASS_NAME;
+ String methodName = AotTestContextInitializersCodeGenerator.GET_CONTEXT_INITIALIZER_CLASSES_METHOD_NAME;
return GeneratedMapUtils.loadMap(className, methodName);
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/DefaultCacheAwareContextLoaderDelegate.java b/spring-test/src/main/java/org/springframework/test/context/cache/DefaultCacheAwareContextLoaderDelegate.java
index 4f9f6784f2..6566ed4dfd 100644
--- a/spring-test/src/main/java/org/springframework/test/context/cache/DefaultCacheAwareContextLoaderDelegate.java
+++ b/spring-test/src/main/java/org/springframework/test/context/cache/DefaultCacheAwareContextLoaderDelegate.java
@@ -209,8 +209,7 @@ public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContext
Class> testClass = mergedConfig.getTestClass();
if (this.aotTestContextInitializers.isSupportedTestClass(testClass)) {
Class extends ApplicationContextInitializer>> contextInitializerClass =
- (Class extends ApplicationContextInitializer>>)
- this.aotTestContextInitializers.getContextInitializer(testClass).getClass();
+ this.aotTestContextInitializers.getContextInitializerClass(testClass);
return new AotMergedContextConfiguration(testClass, contextInitializerClass, mergedConfig, this);
}
return mergedConfig;