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,15 +56,6 @@ void registerDynamicPropertySources(BeanDefinitionRegistry beanDefinitionRegistr
7756 ReflectionUtils .makeAccessible (method );
7857 ReflectionUtils .invokeMethod (method , null , dynamicPropertyRegistry );
7958 });
80- String beanName = "%s.%s" .formatted (DynamicPropertySourceMetadata .class .getName (), definitionClass );
81- if (!beanDefinitionRegistry .containsBeanDefinition (beanName )) {
82- RootBeanDefinition bd = new RootBeanDefinition (DynamicPropertySourceMetadata .class );
83- bd .setInstanceSupplier (() -> new DynamicPropertySourceMetadata (definitionClass , methods ));
84- bd .setRole (BeanDefinition .ROLE_INFRASTRUCTURE );
85- bd .setAutowireCandidate (false );
86- bd .setAttribute (DynamicPropertySourceMetadata .class .getName (), true );
87- beanDefinitionRegistry .registerBeanDefinition (beanName , bd );
88- }
8959 }
9060
9161 private boolean isAnnotated (Method method ) {
@@ -101,132 +71,4 @@ private void assertValid(Method method) {
10171 + "' must accept a single DynamicPropertyRegistry argument" );
10272 }
10373
104- private record DynamicPropertySourceMetadata (Class <?> definitionClass , Set <Method > methods ) {
105- }
106-
107- /**
108- * {@link BeanRegistrationExcludeFilter} to exclude
109- * {@link DynamicPropertySourceMetadata} from AOT bean registrations.
110- */
111- static class DynamicPropertySourceMetadataBeanRegistrationExcludeFilter implements BeanRegistrationExcludeFilter {
112-
113- @ Override
114- public boolean isExcludedFromAotProcessing (RegisteredBean registeredBean ) {
115- return registeredBean .getMergedBeanDefinition ().hasAttribute (DynamicPropertySourceMetadata .class .getName ());
116- }
117-
118- }
119-
120- /**
121- * The {@link BeanFactoryInitializationAotProcessor} generates methods for each
122- * {@code @DynamicPropertySource-annotated} method.
123- *
124- */
125- static class DynamicPropertySourceBeanFactoryInitializationAotProcessor
126- implements BeanFactoryInitializationAotProcessor {
127-
128- private static final String DYNAMIC_PROPERTY_REGISTRY = "dynamicPropertyRegistry" ;
129-
130- @ Override
131- public BeanFactoryInitializationAotContribution processAheadOfTime (
132- ConfigurableListableBeanFactory beanFactory ) {
133- Map <String , DynamicPropertySourceMetadata > metadata = beanFactory
134- .getBeansOfType (DynamicPropertySourceMetadata .class , false , false );
135- if (metadata .isEmpty ()) {
136- return null ;
137- }
138- return new AotContribution (metadata );
139- }
140-
141- private static final class AotContribution implements BeanFactoryInitializationAotContribution {
142-
143- private final Map <String , DynamicPropertySourceMetadata > metadata ;
144-
145- private AotContribution (Map <String , DynamicPropertySourceMetadata > metadata ) {
146- this .metadata = metadata ;
147- }
148-
149- @ Override
150- public void applyTo (GenerationContext generationContext ,
151- BeanFactoryInitializationCode beanFactoryInitializationCode ) {
152- GeneratedMethod initializerMethod = beanFactoryInitializationCode .getMethods ()
153- .add ("registerDynamicPropertySources" , (code ) -> {
154- code .addJavadoc ("Registers {@code @DynamicPropertySource} properties" );
155- code .addParameter (ConfigurableEnvironment .class , "environment" );
156- code .addParameter (DefaultListableBeanFactory .class , "beanFactory" );
157- code .addModifiers (javax .lang .model .element .Modifier .PRIVATE ,
158- javax .lang .model .element .Modifier .STATIC );
159- code .addStatement ("$T dynamicPropertyRegistry = $T.attach(environment, beanFactory)" ,
160- DynamicPropertyRegistry .class , TestcontainersPropertySource .class );
161- this .metadata .forEach ((name , metadata ) -> {
162- GeneratedMethod dynamicPropertySourceMethod = generateMethods (generationContext , metadata );
163- code .addStatement (dynamicPropertySourceMethod .toMethodReference ()
164- .toInvokeCodeBlock (ArgumentCodeGenerator .of (DynamicPropertyRegistry .class ,
165- DYNAMIC_PROPERTY_REGISTRY )));
166- });
167- });
168- beanFactoryInitializationCode .addInitializer (initializerMethod .toMethodReference ());
169- }
170-
171- // Generates a new class in definition class package and invokes
172- // all @DynamicPropertySource methods.
173- private GeneratedMethod generateMethods (GenerationContext generationContext ,
174- DynamicPropertySourceMetadata metadata ) {
175- Class <?> definitionClass = metadata .definitionClass ();
176- GeneratedClass generatedClass = generationContext .getGeneratedClasses ()
177- .addForFeatureComponent (DynamicPropertySource .class .getSimpleName (), definitionClass ,
178- (code ) -> code .addModifiers (javax .lang .model .element .Modifier .PUBLIC ));
179- return generatedClass .getMethods ().add ("registerDynamicPropertySource" , (code ) -> {
180- code .addJavadoc ("Registers {@code @DynamicPropertySource} properties for class '$T'" ,
181- definitionClass );
182- code .addParameter (DynamicPropertyRegistry .class , DYNAMIC_PROPERTY_REGISTRY );
183- code .addModifiers (javax .lang .model .element .Modifier .PUBLIC ,
184- javax .lang .model .element .Modifier .STATIC );
185- metadata .methods ().forEach ((method ) -> {
186- GeneratedMethod generateMethod = generateMethod (generationContext , generatedClass , method );
187- code .addStatement (generateMethod .toMethodReference ()
188- .toInvokeCodeBlock (ArgumentCodeGenerator .of (DynamicPropertyRegistry .class ,
189- DYNAMIC_PROPERTY_REGISTRY )));
190- });
191- });
192- }
193-
194- // If the method is inaccessible, the reflection will be used; otherwise,
195- // direct call to the method will be used.
196- private static GeneratedMethod generateMethod (GenerationContext generationContext ,
197- GeneratedClass generatedClass , Method method ) {
198- return generatedClass .getMethods ().add (method .getName (), (code ) -> {
199- code .addJavadoc ("Register {@code @DynamicPropertySource} for method '$T.$L'" ,
200- method .getDeclaringClass (), method .getName ());
201- code .addModifiers (javax .lang .model .element .Modifier .PRIVATE ,
202- javax .lang .model .element .Modifier .STATIC );
203- code .addParameter (DynamicPropertyRegistry .class , DYNAMIC_PROPERTY_REGISTRY );
204- if (isMethodAccessible (generatedClass , method )) {
205- code .addStatement (CodeBlock .of ("$T.$L($L)" , method .getDeclaringClass (), method .getName (),
206- DYNAMIC_PROPERTY_REGISTRY ));
207- }
208- else {
209- generationContext .getRuntimeHints ().reflection ().registerMethod (method , ExecutableMode .INVOKE );
210- code .addStatement ("$T<?> clazz = $T.resolveClassName($S, $T.class.getClassLoader())" ,
211- Class .class , ClassUtils .class , method .getDeclaringClass ().getTypeName (),
212- generatedClass .getName ());
213- // ReflectionTestUtils can be used here because
214- // @DynamicPropertyRegistry in a test module.
215- code .addStatement ("$T.invokeMethod(clazz, $S, $L)" , ReflectionTestUtils .class , method .getName (),
216- DYNAMIC_PROPERTY_REGISTRY );
217- }
218- });
219-
220- }
221-
222- private static boolean isMethodAccessible (GeneratedClass generatedClass , Method method ) {
223- ClassName className = generatedClass .getName ();
224- return AccessControl .forClass (method .getDeclaringClass ()).isAccessibleFrom (className )
225- && AccessControl .forMember (method ).isAccessibleFrom (className );
226- }
227-
228- }
229-
230- }
231-
23274}
0 commit comments