Skip to content

Commit 35a0fed

Browse files
Add documentation and fix issues after rebase
1 parent 274986b commit 35a0fed

15 files changed

+300
-185
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
= Ahead of Time Optimizations
2+
3+
This chapter covers Spring Data's Ahead of Time (AOT) optimizations that build upon {spring-framework-docs}/core/aot.html[Spring's Ahead of Time Optimizations].
4+
5+
[[aot.bestpractices]]
6+
== Best Practices
7+
8+
=== Annotate your Domain Types
9+
10+
During application startup, Spring scans the classpath for domain classes for early processing of entities.
11+
By annotating your domain types with Spring Data Store specific `@Table`, `@Document` or `@Entity` annotations you can aid initial entity scanning and ensure that those types are registered with `ManagedTypes` for Runtime Hints.
12+
Classpath scanning is not possible in native image arrangements and so Spring has to use `ManagedTypes` for the initial entity set.
13+
14+
[[aot.code-gen]]
15+
== Ahead of Time Code Generation
16+
17+
Ahead of time code generation is not limited to usage with GraalVM Native Image but also offers benefits when working with regular deployments and can help optimize startup performance on the jvm.
18+
19+
If Ahead of Time compilation is enabled Spring Data can (depending on the actual Module in use) contribute several components during the AOT phase of your build.
20+
21+
* Bytecode for generated Type/Property Accessors
22+
* Sourcecode for the defined Repository Interfaces
23+
* Repository Metadata in JSON format
24+
25+
Each of the above is enabled by default.
26+
However there users may fine tune the configuration with following options.
27+
28+
[options = "autowidth",cols="1,1"]
29+
|===
30+
|spring.aot.data.accessors.enabled
31+
|boolean flag to control contribution of Bytecode for generated Type/Property Accessors
32+
33+
|spring.aot.data.accessors.exclude
34+
|comma separated list of FQCN for which to skip contribution of Bytecode for generated Type/Property Accessors
35+
36+
|spring.aot.data.accessors.include
37+
|comma separated list of FQCN for which to contribute Bytecode for generated Type/Property Accessors
38+
39+
|spring.aot.repositories.enabled
40+
|boolean flag to control contribution of Source Code for Repository Interfaces
41+
42+
|spring.aot.[module-name].repositories.enabled
43+
|boolean flag to control contribution of Source Code for Repository Interfaces for a certain module (eg. jdbc)
44+
|===
45+
46+
[[aot.repositories]]
47+
== Ahead of Time Repositories
48+
49+
AOT Repositories are an extension to AOT processing by pre-generating eligible query method implementations.
50+
Query methods are opaque to developers regarding their underlying queries being executed in a query method call.
51+
AOT repositories contribute query method implementations based on derived, annotated, and named queries that are known at build-time.
52+
This optimization moves query method processing from runtime to build-time, which can lead to a significant performance improvement as query methods do not need to be analyzed reflectively upon each application start.
53+
54+
The resulting AOT repository fragment follows the naming scheme of `<Repository FQCN>Impl_AotRepository` and is placed in the same package as the repository interface.
55+
56+
[[aot.hints]]
57+
== Native Image Runtime Hints
58+
59+
Running an application as a native image requires additional information compared to a regular JVM runtime.
60+
Spring Data contributes {spring-framework-docs}/core/aot.html#aot.hints[Runtime Hints] during AOT processing for native image usage.
61+
These are in particular hints for:
62+
63+
* Auditing
64+
* `ManagedTypes` to capture the outcome of class-path scans
65+
* Repositories
66+
** Reflection hints for entities, return types, and Spring Data annotations
67+
** Repository fragments
68+
** Querydsl `Q` classes
69+
** Kotlin Coroutine support
70+
* Web support (Jackson Hints for `PagedModel`)
71+

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.function.Consumer;
2525

2626
import org.jspecify.annotations.Nullable;
27-
import org.springframework.aot.hint.TypeReference;
2827
import org.springframework.beans.factory.BeanFactory;
2928
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3029
import org.springframework.beans.factory.config.BeanDefinition;

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.springframework.data.mapping.model.EntityInstantiators;
2929
import org.springframework.data.mapping.model.Property;
3030
import org.springframework.data.mapping.model.SimpleTypeHolder;
31-
import org.springframework.data.repository.aot.generate.RepositoryContributor;
3231
import org.springframework.data.util.TypeInformation;
3332

3433
/**

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

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2025. the original author or authors.
2+
* Copyright 2025-present the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -31,27 +31,73 @@
3131
import org.springframework.data.projection.TargetAware;
3232

3333
/**
34+
* Configuration object that captures various AOT configuration aspects of types within the data context by offering
35+
* predefined methods to register native configuration necessary for data binding, projection proxy definitions, AOT
36+
* cglib bytecode generation and other common tasks.
37+
* <p>
38+
* On {@link #contribute(Environment, GenerationContext)} the configuration is added to the {@link GenerationContext}.
39+
*
3440
* @author Christoph Strobl
41+
* @since 4.0
3542
*/
3643
public interface AotTypeConfiguration {
3744

45+
/**
46+
* Configure the referenced type for data binding. In case of {@link java.lang.annotation.Annotation} only data ones
47+
* are considered. For more fine grained control use {@link #forReflectiveAccess(MemberCategory...)}.
48+
*
49+
* @return this.
50+
*/
3851
AotTypeConfiguration forDataBinding();
3952

53+
/**
54+
* Configure the referenced type for reflective access by providing at least one {@link MemberCategory}.
55+
*
56+
* @param categories must not contain {@literal null}.
57+
* @return this.
58+
*/
4059
AotTypeConfiguration forReflectiveAccess(MemberCategory... categories);
4160

61+
/**
62+
* Contribute generated cglib accessors for the referenced type.
63+
* <p>
64+
* Can be disabled by user configuration ({@code spring.aot.data.accessors.enabled}). Honors in/exclusions set by user
65+
* configuration {@code spring.aot.data.accessors.include} / {@code spring.aot.data.accessors.exclude}
66+
*
67+
* @return this.
68+
*/
4269
AotTypeConfiguration contributeAccessors();
4370

4471
// TODO: ? should this be a global condition for the entire configuration or do we need it for certain aspects ?
4572
AotTypeConfiguration filter(Predicate<Class<?>> filter);
4673

74+
/**
75+
* Configure the referenced type as a projection interface returned by eg. a query method.
76+
* <p>
77+
* Shortcut for {@link #proxyInterface(Class[]) proxyInterface(TargetAware, SpringProxy, DecoratingProxy)}
78+
*
79+
* @return this.
80+
*/
4781
default AotTypeConfiguration usedAsProjectionInterface() {
4882
return proxyInterface(TargetAware.class, SpringProxy.class, DecoratingProxy.class);
4983
}
5084

85+
/**
86+
* Configure the referenced type as a spring proxy interface.
87+
* <p>
88+
* Shortcut for {@link #proxyInterface(Class[]) proxyInterface(SpringProxy, Advised, DecoratingProxy)}
89+
*
90+
* @return this.
91+
*/
5192
default AotTypeConfiguration springProxy() {
5293
return proxyInterface(SpringProxy.class, Advised.class, DecoratingProxy.class);
5394
}
5495

96+
/**
97+
* Configure the referenced type as a repository proxy.
98+
*
99+
* @return this.
100+
*/
55101
default AotTypeConfiguration repositoryProxy() {
56102

57103
springProxy();
@@ -67,14 +113,36 @@ default AotTypeConfiguration repositoryProxy() {
67113
return this;
68114
}
69115

116+
/**
117+
* Register a proxy for the referenced type that also implements the given proxyInterfaces.
118+
*
119+
* @param proxyInterfaces additional interfaces the proxy implements. Order matters!
120+
* @return this.
121+
*/
70122
AotTypeConfiguration proxyInterface(List<TypeReference> proxyInterfaces);
71123

124+
/**
125+
* Register a proxy for the referenced type that also implements the given proxyInterfaces.
126+
*
127+
* @param proxyInterfaces additional interfaces the proxy implements. Order matters!
128+
* @return this.
129+
*/
72130
default AotTypeConfiguration proxyInterface(Class<?>... proxyInterfaces) {
73131
return proxyInterface(Stream.of(proxyInterfaces).map(TypeReference::of).toList());
74132
}
75133

134+
/**
135+
* Configure the referenced type for usage with Querydsl by registering hints for potential {@code Q} types.
136+
*
137+
* @return this.
138+
*/
76139
AotTypeConfiguration forQuerydsl();
77140

141+
/**
142+
* Write the configuration to the given {@link GenerationContext}.
143+
*
144+
* @param environment must not be {@literal null}.
145+
* @param generationContext must not be {@literal null}.
146+
*/
78147
void contribute(Environment environment, GenerationContext generationContext);
79-
80148
}

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.util.stream.Stream;
3131

3232
import org.jspecify.annotations.Nullable;
33-
3433
import org.springframework.aot.generate.GenerationContext;
3534
import org.springframework.aot.hint.MemberCategory;
3635
import org.springframework.aot.hint.TypeReference;
@@ -41,8 +40,6 @@
4140
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4241
import org.springframework.beans.factory.support.RootBeanDefinition;
4342
import org.springframework.core.env.Environment;
44-
import org.springframework.core.env.Environment;
45-
import org.springframework.data.mapping.context.MappingContext;
4643
import org.springframework.data.util.QTypeContributor;
4744
import org.springframework.data.util.TypeContributor;
4845
import org.springframework.util.AntPathMatcher;
@@ -53,22 +50,28 @@
5350
* Default {@link AotContext} implementation.
5451
*
5552
* @author Mark Paluch
53+
* @author Christoph Strobl
5654
* @since 3.0
5755
*/
5856
class DefaultAotContext implements AotContext {
5957

60-
private final AotMappingContext mappingContext = new AotMappingContext();;
58+
private final AotMappingContext mappingContext;
6159
private final ConfigurableListableBeanFactory factory;
6260

6361
// TODO: should we reuse the config or potentially have multiple ones with different settings - somehow targets the
6462
// filtering issue
6563
private final Map<Class<?>, AotTypeConfiguration> typeConfigurations = new HashMap<>();
66-
private final Environment environment;
64+
private final Environment environment;
6765

6866
public DefaultAotContext(BeanFactory beanFactory, Environment environment) {
67+
this(beanFactory, environment, new AotMappingContext());
68+
}
69+
70+
DefaultAotContext(BeanFactory beanFactory, Environment environment, AotMappingContext mappingContext) {
6971
this.factory = beanFactory instanceof ConfigurableListableBeanFactory cbf ? cbf
7072
: new DefaultListableBeanFactory(beanFactory);
7173
this.environment = environment;
74+
this.mappingContext = mappingContext;
7275
}
7376

7477
@Override
@@ -255,11 +258,7 @@ public void contribute(Environment environment, GenerationContext generationCont
255258
}
256259

257260
if (forDataBinding) {
258-
259261
TypeContributor.contribute(type, Set.of(TypeContributor.DATA_NAMESPACE), generationContext);
260-
261-
generationContext.getRuntimeHints().reflection().registerType(type, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
262-
MemberCategory.INVOKE_DECLARED_METHODS);
263262
}
264263

265264
if (forQuerydsl) {
@@ -268,9 +267,8 @@ public void contribute(Environment environment, GenerationContext generationCont
268267

269268
if (!proxies.isEmpty()) {
270269
for (List<TypeReference> proxyInterfaces : proxies) {
271-
generationContext.getRuntimeHints().proxies()
272-
.registerJdkProxy(Stream.concat(Stream.of(TypeReference.of(type)), proxyInterfaces.stream())
273-
.toArray(TypeReference[]::new));
270+
generationContext.getRuntimeHints().proxies().registerJdkProxy(
271+
Stream.concat(Stream.of(TypeReference.of(type)), proxyInterfaces.stream()).toArray(TypeReference[]::new));
274272
}
275273
}
276274

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.apache.commons.logging.Log;
2323
import org.apache.commons.logging.LogFactory;
2424
import org.jspecify.annotations.Nullable;
25-
2625
import org.springframework.aot.generate.GenerationContext;
2726
import org.springframework.beans.factory.BeanCreationException;
2827
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
@@ -70,11 +69,6 @@ public void setEnvironment(Environment environment) {
7069
this.environment = Lazy.of(environment);
7170
}
7271

73-
@Override
74-
public void setEnvironment(Environment environment) {
75-
this.environment = Lazy.of(() -> environment);
76-
}
77-
7872
@Override
7973
public @Nullable BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
8074

@@ -153,7 +147,7 @@ protected void contributeType(ResolvableType type, GenerationContext generationC
153147
.contributeAccessors() //
154148
.forQuerydsl().contribute(environment.get(), generationContext));
155149

156-
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
150+
TypeUtils.resolveUsedAnnotations(type.toClass()).forEach(
157151
annotation -> TypeContributor.contribute(annotation.getType(), annotationNamespaces, generationContext));
158152
}
159153

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import javax.lang.model.element.Modifier;
2424

2525
import org.jspecify.annotations.Nullable;
26-
2726
import org.springframework.aot.generate.AccessControl;
2827
import org.springframework.aot.generate.GeneratedMethod;
2928
import org.springframework.aot.generate.GenerationContext;
@@ -80,8 +79,8 @@ class ManagedTypesRegistrationAotContribution implements RegisteredBeanAotContri
8079
private final BiConsumer<ResolvableType, GenerationContext> contributionAction;
8180
private final RegisteredBean source;
8281

83-
public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes, RegisteredBean registeredBean,
84-
BiConsumer<ResolvableType, GenerationContext> contributionAction) {
82+
public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes,
83+
RegisteredBean registeredBean, BiConsumer<ResolvableType, GenerationContext> contributionAction) {
8584

8685
this.aotContext = aotContext;
8786
this.managedTypes = managedTypes;
@@ -145,7 +144,8 @@ protected ManagedTypesInstanceCodeFragment(List<Class<?>> sourceTypes, Registere
145144
}
146145

147146
@Override
148-
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
147+
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
148+
BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
149149

150150
GeneratedMethod generatedMethod = beanRegistrationCode.getMethods().add("Instance",
151151
this::generateInstanceFactory);

0 commit comments

Comments
 (0)