18
18
19
19
import java .lang .reflect .Method ;
20
20
import java .lang .reflect .Modifier ;
21
- import java .util .Map ;
22
21
import java .util .Set ;
23
22
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 ;
36
23
import 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 ;
40
24
import org .springframework .boot .testcontainers .properties .TestcontainersPropertySource ;
41
25
import org .springframework .core .MethodIntrospector ;
42
26
import org .springframework .core .annotation .MergedAnnotations ;
43
- import org .springframework .core .env .ConfigurableEnvironment ;
44
27
import org .springframework .core .env .Environment ;
45
- import org .springframework .javapoet .ClassName ;
46
- import org .springframework .javapoet .CodeBlock ;
47
28
import org .springframework .test .context .DynamicPropertyRegistry ;
48
29
import org .springframework .test .context .DynamicPropertySource ;
49
- import org .springframework .test .util .ReflectionTestUtils ;
50
30
import org .springframework .util .Assert ;
51
- import org .springframework .util .ClassUtils ;
52
31
import org .springframework .util .ReflectionUtils ;
53
32
54
33
/**
@@ -77,15 +56,6 @@ void registerDynamicPropertySources(BeanDefinitionRegistry beanDefinitionRegistr
77
56
ReflectionUtils .makeAccessible (method );
78
57
ReflectionUtils .invokeMethod (method , null , dynamicPropertyRegistry );
79
58
});
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
- }
89
59
}
90
60
91
61
private boolean isAnnotated (Method method ) {
@@ -101,132 +71,4 @@ private void assertValid(Method method) {
101
71
+ "' must accept a single DynamicPropertyRegistry argument" );
102
72
}
103
73
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
-
232
74
}
0 commit comments