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,16 +56,6 @@ void registerDynamicPropertySources(BeanDefinitionRegistry beanDefinitionRegistr
77
56
ReflectionUtils .makeAccessible (method );
78
57
ReflectionUtils .invokeMethod (method , null , dynamicPropertyRegistry );
79
58
});
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
- }
90
59
}
91
60
92
61
private boolean isAnnotated (Method method ) {
@@ -102,135 +71,4 @@ private void assertValid(Method method) {
102
71
+ "' must accept a single DynamicPropertyRegistry argument" );
103
72
}
104
73
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
-
236
74
}
0 commit comments