Skip to content

Commit cc92e3d

Browse files
committed
Refine AOT Repositories infrastructure.
Decouple RepositoryRegistrationAotContribution and RepositoryRegistrationAotProcessor, introduce support class for AotRepositoryContext implementations to better protect AotRepositoryContext implementations from changes to AotContext. Remove contribute method from AotTypeConfiguration and move contribution code to AotContext. Deprecate introspection methods for removal with only a single use in Commons and no usage in other modules. Add more fine-grained customization hooks to RepositoryRegistrationAotProcessor and remove superfluous context objects that aren't necessary in their context.
1 parent 2299e90 commit cc92e3d

17 files changed

+442
-550
lines changed

src/main/java/org/springframework/data/aot/AotContext.java

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.util.function.Consumer;
2525

2626
import org.jspecify.annotations.Nullable;
27+
28+
import org.springframework.aot.generate.GenerationContext;
2729
import org.springframework.beans.factory.BeanFactory;
2830
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2931
import org.springframework.beans.factory.config.BeanDefinition;
@@ -40,9 +42,7 @@
4042

4143
/**
4244
* The context in which the AOT processing happens. Grants access to the {@link ConfigurableListableBeanFactory
43-
* beanFactory} and {@link ClassLoader}. Holds a few convenience methods to check if a type
44-
* {@link TypeIntrospector#isTypePresent() is present} and allows resolution of them through {@link TypeIntrospector}
45-
* and {@link IntrospectedBeanDefinition}.
45+
* beanFactory} and {@link ClassLoader}.
4646
* <p>
4747
* Mainly for internal use within the framework.
4848
*
@@ -99,7 +99,7 @@ static AotContext from(BeanFactory beanFactory, Environment environment) {
9999
* @param moduleName name of the module. Can be {@literal null} or {@literal empty}, in which case it will only check
100100
* the general {@link #GENERATED_REPOSITORIES_ENABLED} flag.
101101
* @return indicator if repository code generation is enabled.
102-
* @since 5.0
102+
* @since 4.0
103103
*/
104104
default boolean isGeneratedRepositoriesEnabled(@Nullable String moduleName) {
105105

@@ -119,13 +119,13 @@ default boolean isGeneratedRepositoriesEnabled(@Nullable String moduleName) {
119119
}
120120

121121
/**
122-
* Checks if repository metadata file writing is enabled by checking environment variables for general
123-
* enablement ({@link #GENERATED_REPOSITORIES_JSON_ENABLED})
122+
* Checks if repository metadata file writing is enabled by checking environment variables for general enablement
123+
* ({@link #GENERATED_REPOSITORIES_JSON_ENABLED})
124124
* <p>
125125
* Unset properties are considered being {@literal true}.
126126
*
127127
* @return indicator if repository metadata should be written
128-
* @since 5.0
128+
* @since 4.0
129129
*/
130130
default boolean isGeneratedRepositoriesMetadataEnabled() {
131131
return getEnvironment().getProperty(GENERATED_REPOSITORIES_JSON_ENABLED, Boolean.class, true);
@@ -177,8 +177,12 @@ default ClassLoader getRequiredClassLoader() {
177177
*
178178
* @param typeName {@link String name} of the {@link Class type} to evaluate; must not be {@literal null}.
179179
* @return the type introspector for further type-based introspection.
180+
* @deprecated since 4.0 as this isn't widely used and can be easily implemented within user code.
180181
*/
181-
TypeIntrospector introspectType(String typeName);
182+
@Deprecated(since = "4.0", forRemoval = true)
183+
default TypeIntrospector introspectType(String typeName) {
184+
throw new UnsupportedOperationException(); // preparation for implementation removal.
185+
}
182186

183187
/**
184188
* Returns a new {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT
@@ -201,7 +205,9 @@ default TypeScanner getTypeScanner() {
201205
* @param packageNames {@link Collection} of {@link String package names} to scan.
202206
* @return a {@link Set} of {@link Class types} found during the scan.
203207
* @see #getTypeScanner()
208+
* @deprecated since 4.0, use {@link #getTypeScanner()} directly
204209
*/
210+
@Deprecated(since = "4.0", forRemoval = true)
205211
default Set<Class<?>> scanPackageForTypes(Collection<Class<? extends Annotation>> identifyingAnnotations,
206212
Collection<String> packageNames) {
207213

@@ -214,7 +220,9 @@ default Set<Class<?>> scanPackageForTypes(Collection<Class<? extends Annotation>
214220
*
215221
* @param reference {@link BeanReference} to the managed bean.
216222
* @return the introspected bean definition.
223+
* @deprecated since 4.0, use {@link #getBeanFactory()} and interact with the bean factory directly.
217224
*/
225+
@Deprecated(since = "4.0", forRemoval = true)
218226
default IntrospectedBeanDefinition introspectBeanDefinition(BeanReference reference) {
219227
return introspectBeanDefinition(reference.getBeanName());
220228
}
@@ -225,40 +233,50 @@ default IntrospectedBeanDefinition introspectBeanDefinition(BeanReference refere
225233
*
226234
* @param beanName {@link String} containing the {@literal name} of the bean to evaluate; must not be {@literal null}.
227235
* @return the introspected bean definition.
236+
* @deprecated since 4.0, use {@link #getBeanFactory()} and interact with the bean factory directly.
228237
*/
229-
IntrospectedBeanDefinition introspectBeanDefinition(String beanName);
238+
@Deprecated(since = "4.0", forRemoval = true)
239+
default IntrospectedBeanDefinition introspectBeanDefinition(String beanName) {
240+
throw new UnsupportedOperationException(); // preparation for implementation removal.
241+
}
230242

231243
/**
232244
* Obtain a {@link AotTypeConfiguration} for the given {@link ResolvableType} to customize the AOT processing for the
233-
* given type.
245+
* given type. Repeated calls to the same type will result in merging the configuration.
234246
*
235247
* @param resolvableType the resolvable type to configure.
236248
* @param configurationConsumer configuration consumer function.
249+
* @since 4.0
237250
*/
238251
default void typeConfiguration(ResolvableType resolvableType, Consumer<AotTypeConfiguration> configurationConsumer) {
239252
typeConfiguration(resolvableType.toClass(), configurationConsumer);
240253
}
241254

242255
/**
243256
* Obtain a {@link AotTypeConfiguration} for the given {@link ResolvableType} to customize the AOT processing for the
244-
* given type.
257+
* given type. Repeated calls to the same type will result in merging the configuration.
245258
*
246259
* @param type the type to configure.
247260
* @param configurationConsumer configuration consumer function.
261+
* @since 4.0
248262
*/
249263
void typeConfiguration(Class<?> type, Consumer<AotTypeConfiguration> configurationConsumer);
250264

251265
/**
252-
* Return all type configurations registered with this {@link AotContext}.
266+
* Contribute type configurations to the given {@link GenerationContext}. This method is called once per
267+
* {@link GenerationContext} after all type configurations have been registered.
253268
*
254-
* @return all type configurations registered with this {@link AotContext}.
269+
* @param generationContext the context to contribute the type configurations to.
255270
*/
256-
Collection<AotTypeConfiguration> typeConfigurations();
271+
void contributeTypeConfigurations(GenerationContext generationContext);
257272

258273
/**
259274
* Type-based introspector to resolve {@link Class} from a type name and to introspect the bean factory for presence
260275
* of beans.
276+
*
277+
* @deprecated since 4.0 as this isn't widely used and can be easily implemented within user code.
261278
*/
279+
@Deprecated(since = "4.0", forRemoval = true)
262280
interface TypeIntrospector {
263281

264282
/**
@@ -318,7 +336,10 @@ default void ifTypePresent(Consumer<Class<?>> action) {
318336

319337
/**
320338
* Interface defining introspection methods for bean definitions.
339+
*
340+
* @deprecated since 4.0 as this isn't widely used and can be easily implemented within user code.
321341
*/
342+
@Deprecated(since = "4.0", forRemoval = true)
322343
interface IntrospectedBeanDefinition {
323344

324345
/**

src/main/java/org/springframework/data/aot/AotTypeConfiguration.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,15 @@
2222

2323
import org.springframework.aop.SpringProxy;
2424
import org.springframework.aop.framework.Advised;
25-
import org.springframework.aot.generate.GenerationContext;
2625
import org.springframework.aot.hint.MemberCategory;
2726
import org.springframework.aot.hint.TypeReference;
2827
import org.springframework.core.DecoratingProxy;
29-
import org.springframework.core.env.Environment;
3028
import org.springframework.data.projection.TargetAware;
3129

3230
/**
3331
* Configuration object that captures various AOT configuration aspects of types within the data context by offering
3432
* predefined methods to register native configuration necessary for data binding, projection proxy definitions, AOT
3533
* cglib bytecode generation and other common tasks.
36-
* <p>
37-
* On {@link #contribute(Environment, GenerationContext)} the configuration is added to the {@link GenerationContext}.
3834
*
3935
* @author Christoph Strobl
4036
* @since 4.0
@@ -134,11 +130,4 @@ default AotTypeConfiguration proxyInterface(Class<?>... proxyInterfaces) {
134130
*/
135131
AotTypeConfiguration forQuerydsl();
136132

137-
/**
138-
* Write the configuration to the given {@link GenerationContext}.
139-
*
140-
* @param environment must not be {@literal null}.
141-
* @param generationContext must not be {@literal null}.
142-
*/
143-
void contribute(Environment environment, GenerationContext generationContext);
144133
}

src/main/java/org/springframework/data/aot/DefaultAotContext.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Arrays;
20-
import java.util.Collection;
2120
import java.util.Collections;
2221
import java.util.HashMap;
2322
import java.util.HashSet;
@@ -30,6 +29,7 @@
3029
import java.util.stream.Stream;
3130

3231
import org.jspecify.annotations.Nullable;
32+
3333
import org.springframework.aot.generate.GenerationContext;
3434
import org.springframework.aot.hint.MemberCategory;
3535
import org.springframework.aot.hint.TypeReference;
@@ -55,13 +55,13 @@
5555
* @author Christoph Strobl
5656
* @since 3.0
5757
*/
58+
@SuppressWarnings("removal")
5859
class DefaultAotContext implements AotContext {
5960

6061
private final AotMappingContext mappingContext;
6162
private final ConfigurableListableBeanFactory factory;
6263

63-
// TODO: should we reuse the config or potentially have multiple ones with different settings for the same type
64-
private final Map<Class<?>, AotTypeConfiguration> typeConfigurations = new HashMap<>();
64+
private final Map<Class<?>, ContextualTypeConfiguration> typeConfigurations = new HashMap<>();
6565
private final Environment environment;
6666

6767
public DefaultAotContext(BeanFactory beanFactory, Environment environment) {
@@ -101,10 +101,13 @@ public void typeConfiguration(Class<?> type, Consumer<AotTypeConfiguration> conf
101101
}
102102

103103
@Override
104-
public Collection<AotTypeConfiguration> typeConfigurations() {
105-
return typeConfigurations.values();
104+
public void contributeTypeConfigurations(GenerationContext generationContext) {
105+
typeConfigurations.forEach((type, configuration) -> {
106+
configuration.contribute(this.environment, generationContext);
107+
});
106108
}
107109

110+
@SuppressWarnings("removal")
108111
class DefaultTypeIntrospector implements TypeIntrospector {
109112

110113
private final String typeName;
@@ -144,6 +147,7 @@ public List<String> getBeanNames() {
144147
}
145148
}
146149

150+
@SuppressWarnings("removal")
147151
class DefaultIntrospectedBeanDefinition implements IntrospectedBeanDefinition {
148152

149153
private final String beanName;
@@ -227,7 +231,6 @@ public AotTypeConfiguration forQuerydsl() {
227231
return this;
228232
}
229233

230-
@Override
231234
public void contribute(Environment environment, GenerationContext generationContext) {
232235

233236
try {

src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,7 @@ protected void contributeType(ResolvableType type, GenerationContext generationC
155155
Set<String> annotationNamespaces = Collections.singleton(TypeContributor.DATA_NAMESPACE);
156156

157157
configureTypeContribution(type.toClass(), aotContext);
158-
159-
aotContext.typeConfiguration(type, config -> {
160-
config.contribute(environment.get(), generationContext);
161-
});
158+
aotContext.contributeTypeConfigurations(generationContext);
162159

163160
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
164161
annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext));

src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryBeanDefinitionPropertiesDecorator.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141

4242
/**
4343
* Delegate to decorate AOT {@code BeanDefinition} properties during AOT processing. Adds a {@link CodeBlock} for the
44-
* fragment function that resolves {@link RepositoryContributor#requiredArgs()} from the {@link BeanFactory} and
45-
* provides them to the generated repository fragment.
44+
* fragment function that resolves {@link RepositoryContributor#getAotFragmentMetadata()} from the {@link BeanFactory}
45+
* and provides them to the generated repository fragment.
4646
*
4747
* @author Mark Paluch
4848
* @author Christoph Strobl
@@ -128,7 +128,8 @@ private CodeBlock buildCallbackBody() {
128128
CodeBlock.Builder callback = CodeBlock.builder();
129129
List<Object> arguments = new ArrayList<>();
130130

131-
for (Entry<String, ConstructorArgument> entry : repositoryContributor.getConstructorArguments().entrySet()) {
131+
for (Entry<String, ConstructorArgument> entry : repositoryContributor.getAotFragmentMetadata()
132+
.getConstructorArguments().entrySet()) {
132133

133134
ConstructorArgument argument = entry.getValue();
134135
AotRepositoryConstructorBuilder.ParameterOrigin parameterOrigin = argument.parameterOrigin();

src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryCreator.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
2121
import java.util.Comparator;
22-
import java.util.LinkedHashMap;
2322
import java.util.List;
24-
import java.util.Map;
2523
import java.util.function.Consumer;
2624

2725
import javax.lang.model.element.Modifier;
@@ -30,9 +28,7 @@
3028
import org.apache.commons.logging.LogFactory;
3129
import org.jspecify.annotations.Nullable;
3230

33-
import org.springframework.core.ResolvableType;
3431
import org.springframework.data.projection.ProjectionFactory;
35-
import org.springframework.data.repository.aot.generate.AotRepositoryFragmentMetadata.ConstructorArgument;
3632
import org.springframework.data.repository.core.RepositoryInformation;
3733
import org.springframework.data.repository.core.support.RepositoryComposition;
3834
import org.springframework.data.repository.core.support.RepositoryFragment;
@@ -100,18 +96,8 @@ String packageName() {
10096
return repositoryInformation.getRepositoryInterface().getPackageName();
10197
}
10298

103-
Map<String, ResolvableType> getAutowireFields() {
104-
105-
Map<String, ResolvableType> autowireFields = new LinkedHashMap<>(
106-
generationMetadata.getConstructorArguments().size());
107-
for (Map.Entry<String, ConstructorArgument> entry : generationMetadata.getConstructorArguments().entrySet()) {
108-
autowireFields.put(entry.getKey(), entry.getValue().parameterType());
109-
}
110-
return autowireFields;
111-
}
112-
113-
Map<String, ConstructorArgument> getConstructorArguments() {
114-
return generationMetadata.getConstructorArguments();
99+
AotRepositoryFragmentMetadata getRepositoryMetadata() {
100+
return generationMetadata;
115101
}
116102

117103
RepositoryInformation getRepositoryInformation() {

src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryFragmentMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ public Map<String, ConstructorArgument> getConstructorArguments() {
129129
return constructorArguments;
130130
}
131131

132+
Map<String, ResolvableType> getAutowireFields() {
133+
134+
Map<String, ResolvableType> autowireFields = new LinkedHashMap<>(getConstructorArguments().size());
135+
for (Map.Entry<String, ConstructorArgument> entry : getConstructorArguments().entrySet()) {
136+
autowireFields.put(entry.getKey(), entry.getValue().parameterType());
137+
}
138+
return autowireFields;
139+
}
140+
132141
public Map<String, LocalMethod> getMethods() {
133142
return methods;
134143
}

src/main/java/org/springframework/data/repository/aot/generate/RepositoryContributor.java

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,18 @@
1818
import java.io.ByteArrayInputStream;
1919
import java.lang.reflect.Method;
2020
import java.nio.charset.StandardCharsets;
21-
import java.util.Collections;
2221
import java.util.List;
2322

2423
import org.apache.commons.logging.Log;
2524
import org.apache.commons.logging.LogFactory;
2625
import org.jspecify.annotations.Nullable;
26+
2727
import org.springframework.aot.generate.GeneratedClass;
2828
import org.springframework.aot.generate.GeneratedFiles.Kind;
2929
import org.springframework.aot.generate.GeneratedTypeReference;
3030
import org.springframework.aot.generate.GenerationContext;
3131
import org.springframework.aot.hint.MemberCategory;
3232
import org.springframework.aot.hint.TypeReference;
33-
import org.springframework.core.ResolvableType;
3433
import org.springframework.data.projection.ProjectionFactory;
3534
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
3635
import org.springframework.data.repository.aot.generate.AotRepositoryCreator.AotBundle;
@@ -66,7 +65,7 @@ public class RepositoryContributor {
6665
public RepositoryContributor(AotRepositoryContext repositoryContext) {
6766

6867
this.repositoryContext = repositoryContext;
69-
creator = AotRepositoryCreator.forRepository(repositoryContext.getRepositoryInformation(),
68+
this.creator = AotRepositoryCreator.forRepository(repositoryContext.getRepositoryInformation(),
7069
repositoryContext.getModuleName(), createProjectionFactory());
7170
}
7271

@@ -102,28 +101,10 @@ TypeReference getContributedTypeName() {
102101
}
103102

104103
/**
105-
* Get the required constructor arguments for the to be generated repository implementation. Types will be obtained by
106-
* type from {@link org.springframework.beans.factory.BeanFactory} upon initialization of the generated fragment
107-
* during application startup.
108-
* <p>
109-
* Can be overridden if required. Needs to match arguments of generated repository implementation.
110-
*
111-
* @return key/value pairs of required argument required to instantiate the generated fragment.
112-
*/
113-
// TODO: should we switch from ResolvableType to some custom value object to cover qualifiers?
114-
java.util.Map<String, ResolvableType> requiredArgs() {
115-
return Collections.unmodifiableMap(creator.getAutowireFields());
116-
}
117-
118-
/**
119-
* Get the required constructor arguments for the to be generated repository implementation.
120-
* <p>
121-
* Can be overridden if required. Needs to match arguments of generated repository implementation.
122-
*
123-
* @return key/value pairs of required argument required to instantiate the generated fragment.
104+
* @return the associated {@link AotRepositoryFragmentMetadata}.
124105
*/
125-
java.util.Map<String, AotRepositoryFragmentMetadata.ConstructorArgument> getConstructorArguments() {
126-
return Collections.unmodifiableMap(creator.getConstructorArguments());
106+
AotRepositoryFragmentMetadata getAotFragmentMetadata() {
107+
return creator.getRepositoryMetadata();
127108
}
128109

129110
/**

0 commit comments

Comments
 (0)