16
16
17
17
package org .springframework .test .context .aot ;
18
18
19
+ import java .util .Arrays ;
20
+ import java .util .LinkedHashSet ;
19
21
import java .util .Map ;
22
+ import java .util .Set ;
20
23
import java .util .concurrent .atomic .AtomicInteger ;
21
24
import java .util .stream .Stream ;
22
25
30
33
import org .springframework .aot .generate .GeneratedFiles ;
31
34
import org .springframework .aot .generate .GenerationContext ;
32
35
import org .springframework .aot .hint .RuntimeHints ;
36
+ import org .springframework .aot .hint .RuntimeHintsRegistrar ;
33
37
import org .springframework .aot .hint .TypeReference ;
38
+ import org .springframework .aot .hint .annotation .ReflectiveRuntimeHintsRegistrar ;
39
+ import org .springframework .beans .BeanUtils ;
34
40
import org .springframework .beans .factory .aot .AotServices ;
35
41
import org .springframework .context .ApplicationContext ;
36
42
import org .springframework .context .ApplicationContextInitializer ;
43
+ import org .springframework .context .annotation .ImportRuntimeHints ;
37
44
import org .springframework .context .aot .ApplicationContextAotGenerator ;
38
45
import org .springframework .context .support .GenericApplicationContext ;
46
+ import org .springframework .core .annotation .MergedAnnotation ;
47
+ import org .springframework .core .annotation .MergedAnnotations ;
48
+ import org .springframework .core .annotation .MergedAnnotations .SearchStrategy ;
39
49
import org .springframework .core .log .LogMessage ;
40
50
import org .springframework .javapoet .ClassName ;
41
51
import org .springframework .test .context .BootstrapUtils ;
@@ -117,16 +127,31 @@ public void processAheadOfTime(Stream<Class<?>> testClasses) throws TestContextA
117
127
try {
118
128
resetAotFactories ();
119
129
130
+ Set <Class <? extends RuntimeHintsRegistrar >> coreRuntimeHintsRegistrarClasses = new LinkedHashSet <>();
131
+ ReflectiveRuntimeHintsRegistrar reflectiveRuntimeHintsRegistrar = new ReflectiveRuntimeHintsRegistrar ();
132
+
120
133
MultiValueMap <MergedContextConfiguration , Class <?>> mergedConfigMappings = new LinkedMultiValueMap <>();
121
134
ClassLoader classLoader = getClass ().getClassLoader ();
122
135
testClasses .forEach (testClass -> {
123
136
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration (testClass );
124
137
mergedConfigMappings .add (mergedConfig , testClass );
138
+ collectRuntimeHintsRegistrarClasses (testClass , coreRuntimeHintsRegistrarClasses );
139
+ reflectiveRuntimeHintsRegistrar .registerRuntimeHints (this .runtimeHints , testClass );
125
140
this .testRuntimeHintsRegistrars .forEach (registrar ->
126
141
registrar .registerHints (this .runtimeHints , testClass , classLoader ));
127
142
});
128
- MultiValueMap <ClassName , Class <?>> initializerClassMappings = processAheadOfTime (mergedConfigMappings );
129
143
144
+ coreRuntimeHintsRegistrarClasses .stream ()
145
+ .map (BeanUtils ::instantiateClass )
146
+ .forEach (registrar -> {
147
+ if (logger .isTraceEnabled ()) {
148
+ logger .trace ("Processing RuntimeHints contribution from test class [%s]"
149
+ .formatted (registrar .getClass ().getCanonicalName ()));
150
+ }
151
+ registrar .registerHints (this .runtimeHints , classLoader );
152
+ });
153
+
154
+ MultiValueMap <ClassName , Class <?>> initializerClassMappings = processAheadOfTime (mergedConfigMappings );
130
155
generateAotTestContextInitializerMappings (initializerClassMappings );
131
156
generateAotTestAttributeMappings ();
132
157
}
@@ -135,6 +160,25 @@ public void processAheadOfTime(Stream<Class<?>> testClasses) throws TestContextA
135
160
}
136
161
}
137
162
163
+ /**
164
+ * Collect all {@link RuntimeHintsRegistrar} classes declared via
165
+ * {@link ImportRuntimeHints @ImportRuntimeHints} on the supplied test class
166
+ * and add them to the supplied {@link Set}.
167
+ * @param testClass the test class on which to search for {@code @ImportRuntimeHints}
168
+ * @param coreRuntimeHintsRegistrarClasses the set of registrar classes
169
+ */
170
+ private void collectRuntimeHintsRegistrarClasses (
171
+ Class <?> testClass , Set <Class <? extends RuntimeHintsRegistrar >> coreRuntimeHintsRegistrarClasses ) {
172
+
173
+ MergedAnnotations .from (testClass , SearchStrategy .TYPE_HIERARCHY )
174
+ .stream (ImportRuntimeHints .class )
175
+ .filter (MergedAnnotation ::isPresent )
176
+ .map (MergedAnnotation ::synthesize )
177
+ .map (ImportRuntimeHints ::value )
178
+ .flatMap (Arrays ::stream )
179
+ .forEach (coreRuntimeHintsRegistrarClasses ::add );
180
+ }
181
+
138
182
private void resetAotFactories () {
139
183
AotTestAttributesFactory .reset ();
140
184
AotTestContextInitializersFactory .reset ();
0 commit comments