1818
1919import java .lang .reflect .Method ;
2020import java .lang .reflect .Modifier ;
21- import java .util .Map ;
2221import java .util .Set ;
2322
24- import org .springframework .aot .generate .AccessControl ;
25- import org .springframework .aot .generate .GeneratedClass ;
26- import org .springframework .aot .generate .GeneratedMethod ;
27- import org .springframework .aot .generate .GenerationContext ;
28- import org .springframework .aot .generate .MethodReference .ArgumentCodeGenerator ;
29- import org .springframework .aot .hint .ExecutableMode ;
30- import org .springframework .beans .factory .aot .BeanFactoryInitializationAotContribution ;
31- import org .springframework .beans .factory .aot .BeanFactoryInitializationAotProcessor ;
32- import org .springframework .beans .factory .aot .BeanFactoryInitializationCode ;
33- import org .springframework .beans .factory .aot .BeanRegistrationExcludeFilter ;
34- import org .springframework .beans .factory .config .BeanDefinition ;
35- import org .springframework .beans .factory .config .ConfigurableListableBeanFactory ;
3623import org .springframework .beans .factory .support .BeanDefinitionRegistry ;
37- import org .springframework .beans .factory .support .DefaultListableBeanFactory ;
38- import org .springframework .beans .factory .support .RegisteredBean ;
39- import org .springframework .beans .factory .support .RootBeanDefinition ;
4024import org .springframework .boot .testcontainers .properties .TestcontainersPropertySource ;
4125import org .springframework .core .MethodIntrospector ;
4226import org .springframework .core .annotation .MergedAnnotations ;
43- import org .springframework .core .env .ConfigurableEnvironment ;
4427import org .springframework .core .env .Environment ;
45- import org .springframework .javapoet .ClassName ;
46- import org .springframework .javapoet .CodeBlock ;
4728import org .springframework .test .context .DynamicPropertyRegistry ;
4829import org .springframework .test .context .DynamicPropertySource ;
49- import org .springframework .test .util .ReflectionTestUtils ;
5030import org .springframework .util .Assert ;
51- import org .springframework .util .ClassUtils ;
5231import org .springframework .util .ReflectionUtils ;
5332
5433/**
@@ -77,16 +56,6 @@ void registerDynamicPropertySources(BeanDefinitionRegistry beanDefinitionRegistr
7756 ReflectionUtils .makeAccessible (method );
7857 ReflectionUtils .invokeMethod (method , null , dynamicPropertyRegistry );
7958 });
80-
81- String beanName = "importTestContainer.%s.%s" .formatted (DynamicPropertySource .class .getName (), definitionClass );
82- if (!beanDefinitionRegistry .containsBeanDefinition (beanName )) {
83- RootBeanDefinition bd = new RootBeanDefinition (DynamicPropertySourceMetadata .class );
84- bd .setInstanceSupplier (() -> new DynamicPropertySourceMetadata (definitionClass , methods ));
85- bd .setRole (BeanDefinition .ROLE_INFRASTRUCTURE );
86- bd .setAutowireCandidate (false );
87- bd .setAttribute (DynamicPropertySourceMetadata .class .getName (), true );
88- beanDefinitionRegistry .registerBeanDefinition (beanName , bd );
89- }
9059 }
9160
9261 private boolean isAnnotated (Method method ) {
@@ -102,135 +71,4 @@ private void assertValid(Method method) {
10271 + "' must accept a single DynamicPropertyRegistry argument" );
10372 }
10473
105- private record DynamicPropertySourceMetadata (Class <?> definitionClass , Set <Method > methods ) {
106- }
107-
108- /**
109- * {@link BeanRegistrationExcludeFilter} to exclude
110- * {@link DynamicPropertySourceMetadata} from AOT bean registrations.
111- */
112- static class DynamicPropertySourceMetadataBeanRegistrationExcludeFilter implements BeanRegistrationExcludeFilter {
113-
114- @ Override
115- public boolean isExcludedFromAotProcessing (RegisteredBean registeredBean ) {
116- return registeredBean .getMergedBeanDefinition ().hasAttribute (DynamicPropertySourceMetadata .class .getName ());
117- }
118-
119- }
120-
121- /**
122- * The {@link BeanFactoryInitializationAotProcessor} generates methods for each
123- * {@code @DynamicPropertySource-annotated} method.
124- *
125- */
126- static class DynamicPropertySourceBeanFactoryInitializationAotProcessor
127- implements BeanFactoryInitializationAotProcessor {
128-
129- private static final String DYNAMIC_PROPERTY_REGISTRY = "dynamicPropertyRegistry" ;
130-
131- @ Override
132- public BeanFactoryInitializationAotContribution processAheadOfTime (
133- ConfigurableListableBeanFactory beanFactory ) {
134- Map <String , DynamicPropertySourceMetadata > metadata = beanFactory
135- .getBeansOfType (DynamicPropertySourceMetadata .class , false , false );
136- if (metadata .isEmpty ()) {
137- return null ;
138- }
139- return new AotContibution (metadata );
140- }
141-
142- private static final class AotContibution implements BeanFactoryInitializationAotContribution {
143-
144- private final Map <String , DynamicPropertySourceMetadata > metadata ;
145-
146- private AotContibution (Map <String , DynamicPropertySourceMetadata > metadata ) {
147- this .metadata = metadata ;
148- }
149-
150- @ Override
151- public void applyTo (GenerationContext generationContext ,
152- BeanFactoryInitializationCode beanFactoryInitializationCode ) {
153- GeneratedMethod initializerMethod = beanFactoryInitializationCode .getMethods ()
154- .add ("registerDynamicPropertySources" , (code ) -> {
155- code .addJavadoc ("Registers {@code @DynamicPropertySource} properties" );
156- code .addParameter (ConfigurableEnvironment .class , "environment" );
157- code .addParameter (DefaultListableBeanFactory .class , "beanFactory" );
158- code .addModifiers (javax .lang .model .element .Modifier .PRIVATE ,
159- javax .lang .model .element .Modifier .STATIC );
160- code .addStatement ("$T dynamicPropertyRegistry = $T.attach(environment, beanFactory)" ,
161- DynamicPropertyRegistry .class , TestcontainersPropertySource .class );
162- this .metadata .forEach ((name , metadata ) -> {
163- GeneratedMethod dynamicPropertySourceMethod = generateMethods (generationContext , metadata );
164- code .addStatement (dynamicPropertySourceMethod .toMethodReference ()
165- .toInvokeCodeBlock (ArgumentCodeGenerator .of (DynamicPropertyRegistry .class ,
166- DYNAMIC_PROPERTY_REGISTRY )));
167- });
168- });
169- beanFactoryInitializationCode .addInitializer (initializerMethod .toMethodReference ());
170- }
171-
172- // Generates a new class in definition class package and invokes
173- // all @DynamicPropertySource methods.
174- private GeneratedMethod generateMethods (GenerationContext generationContext ,
175- DynamicPropertySourceMetadata metadata ) {
176- Class <?> definitionClass = metadata .definitionClass ();
177- GeneratedClass generatedClass = generationContext .getGeneratedClasses ()
178- .addForFeatureComponent (DynamicPropertySource .class .getSimpleName (), definitionClass ,
179- (code ) -> code .addModifiers (javax .lang .model .element .Modifier .PUBLIC ));
180- return generatedClass .getMethods ().add ("registerDynamicPropertySource" , (code ) -> {
181- code .addJavadoc ("Registers {@code @DynamicPropertySource} properties for class '$T'" ,
182- definitionClass );
183- code .addParameter (DynamicPropertyRegistry .class , DYNAMIC_PROPERTY_REGISTRY );
184- code .addModifiers (javax .lang .model .element .Modifier .PUBLIC ,
185- javax .lang .model .element .Modifier .STATIC );
186- metadata .methods ().forEach ((method ) -> {
187- GeneratedMethod generateMethod = generateMethod (generationContext , generatedClass , method );
188- code .addStatement (generateMethod .toMethodReference ()
189- .toInvokeCodeBlock (ArgumentCodeGenerator .of (DynamicPropertyRegistry .class ,
190- DYNAMIC_PROPERTY_REGISTRY )));
191- });
192- });
193- }
194-
195- // If the method is inaccessible, the reflection will be used; otherwise,
196- // direct call to the method will be used.
197- private static GeneratedMethod generateMethod (GenerationContext generationContext ,
198- GeneratedClass generatedClass , Method method ) {
199- return generatedClass .getMethods ().add (method .getName (), (code ) -> {
200- code .addJavadoc ("Register {@code @DynamicPropertySource} for method '$T.$L'" ,
201- method .getDeclaringClass (), method .getName ());
202- code .addModifiers (javax .lang .model .element .Modifier .PRIVATE ,
203- javax .lang .model .element .Modifier .STATIC );
204- code .addParameter (DynamicPropertyRegistry .class , DYNAMIC_PROPERTY_REGISTRY );
205- if (isMethodAccessible (generatedClass , method )) {
206- code .addStatement (CodeBlock .of ("$T.$L($L)" , method .getDeclaringClass (), method .getName (),
207- DYNAMIC_PROPERTY_REGISTRY ));
208- }
209- else {
210- generationContext .getRuntimeHints ().reflection ().registerMethod (method , ExecutableMode .INVOKE );
211- code .beginControlFlow ("try" );
212- code .addStatement ("$T<?> clazz = $T.forName($S, $T.class.getClassLoader())" , Class .class ,
213- ClassUtils .class , ClassName .get (method .getDeclaringClass ()), generatedClass .getName ());
214- // ReflectionTestUtils can be used here because
215- // @DynamicPropertyRegistry in a test module.
216- code .addStatement ("$T.invokeMethod(clazz, $S, $L)" , ReflectionTestUtils .class , method .getName (),
217- DYNAMIC_PROPERTY_REGISTRY );
218- code .nextControlFlow ("catch ($T ex)" , ClassNotFoundException .class );
219- code .addStatement ("throw new $T(ex)" , RuntimeException .class );
220- code .endControlFlow ();
221- }
222- });
223-
224- }
225-
226- private static boolean isMethodAccessible (GeneratedClass generatedClass , Method method ) {
227- ClassName className = generatedClass .getName ();
228- return AccessControl .forClass (method .getDeclaringClass ()).isAccessibleFrom (className )
229- && AccessControl .forMember (method ).isAccessibleFrom (className );
230- }
231-
232- }
233-
234- }
235-
23674}
0 commit comments