Skip to content

Commit 4a9b9ab

Browse files
Add documentation and fix issues after rebase
1 parent 274986b commit 4a9b9ab

20 files changed

+341
-216
lines changed

src/main/antora/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* xref:custom-conversions.adoc[]
2222
* xref:entity-callbacks.adoc[]
2323
* xref:is-new-state-detection.adoc[]
24+
* xref:aot.adoc[]
2425
* xref:kotlin.adoc[]
2526
** xref:kotlin/requirements.adoc[]
2627
** xref:kotlin/null-safety.adoc[]
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/antora/modules/ROOT/pages/repositories/custom-implementations.adoc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ The `exposeMetadata` flag can be set directly on the repository factory bean via
362362
import org.springframework.beans.factory.config.BeanPostProcessor;
363363
import org.springframework.context.annotation.Configuration;
364364
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
365-
import org.springframework.lang.Nullable;
366365
367366
@Configuration
368367
class MyConfiguration {

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 & 7 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,
@@ -18,7 +18,6 @@
1818

1919
import java.io.Serializable;
2020
import java.util.List;
21-
import java.util.function.Predicate;
2221
import java.util.stream.Stream;
2322

2423
import org.springframework.aop.SpringProxy;
@@ -31,27 +30,70 @@
3130
import org.springframework.data.projection.TargetAware;
3231

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

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

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

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

44-
// TODO: ? should this be a global condition for the entire configuration or do we need it for certain aspects ?
45-
AotTypeConfiguration filter(Predicate<Class<?>> filter);
46-
70+
/**
71+
* Configure the referenced type as a projection interface returned by eg. a query method.
72+
* <p>
73+
* Shortcut for {@link #proxyInterface(Class[]) proxyInterface(TargetAware, SpringProxy, DecoratingProxy)}
74+
*
75+
* @return this.
76+
*/
4777
default AotTypeConfiguration usedAsProjectionInterface() {
4878
return proxyInterface(TargetAware.class, SpringProxy.class, DecoratingProxy.class);
4979
}
5080

81+
/**
82+
* Configure the referenced type as a spring proxy interface.
83+
* <p>
84+
* Shortcut for {@link #proxyInterface(Class[]) proxyInterface(SpringProxy, Advised, DecoratingProxy)}
85+
*
86+
* @return this.
87+
*/
5188
default AotTypeConfiguration springProxy() {
5289
return proxyInterface(SpringProxy.class, Advised.class, DecoratingProxy.class);
5390
}
5491

92+
/**
93+
* Configure the referenced type as a repository proxy.
94+
*
95+
* @return this.
96+
*/
5597
default AotTypeConfiguration repositoryProxy() {
5698

5799
springProxy();
@@ -67,14 +109,36 @@ default AotTypeConfiguration repositoryProxy() {
67109
return this;
68110
}
69111

112+
/**
113+
* Register a proxy for the referenced type that also implements the given proxyInterfaces.
114+
*
115+
* @param proxyInterfaces additional interfaces the proxy implements. Order matters!
116+
* @return this.
117+
*/
70118
AotTypeConfiguration proxyInterface(List<TypeReference> proxyInterfaces);
71119

120+
/**
121+
* Register a proxy for the referenced type that also implements the given proxyInterfaces.
122+
*
123+
* @param proxyInterfaces additional interfaces the proxy implements. Order matters!
124+
* @return this.
125+
*/
72126
default AotTypeConfiguration proxyInterface(Class<?>... proxyInterfaces) {
73127
return proxyInterface(Stream.of(proxyInterfaces).map(TypeReference::of).toList());
74128
}
75129

130+
/**
131+
* Configure the referenced type for usage with Querydsl by registering hints for potential {@code Q} types.
132+
*
133+
* @return this.
134+
*/
76135
AotTypeConfiguration forQuerydsl();
77136

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+
*/
78143
void contribute(Environment environment, GenerationContext generationContext);
79-
80144
}

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

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
import java.util.Optional;
2727
import java.util.Set;
2828
import java.util.function.Consumer;
29-
import java.util.function.Predicate;
3029
import java.util.stream.Stream;
3130

3231
import org.jspecify.annotations.Nullable;
33-
3432
import org.springframework.aot.generate.GenerationContext;
3533
import org.springframework.aot.hint.MemberCategory;
3634
import org.springframework.aot.hint.TypeReference;
@@ -41,8 +39,6 @@
4139
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4240
import org.springframework.beans.factory.support.RootBeanDefinition;
4341
import org.springframework.core.env.Environment;
44-
import org.springframework.core.env.Environment;
45-
import org.springframework.data.mapping.context.MappingContext;
4642
import org.springframework.data.util.QTypeContributor;
4743
import org.springframework.data.util.TypeContributor;
4844
import org.springframework.util.AntPathMatcher;
@@ -53,22 +49,27 @@
5349
* Default {@link AotContext} implementation.
5450
*
5551
* @author Mark Paluch
52+
* @author Christoph Strobl
5653
* @since 3.0
5754
*/
5855
class DefaultAotContext implements AotContext {
5956

60-
private final AotMappingContext mappingContext = new AotMappingContext();;
57+
private final AotMappingContext mappingContext;
6158
private final ConfigurableListableBeanFactory factory;
6259

63-
// TODO: should we reuse the config or potentially have multiple ones with different settings - somehow targets the
64-
// filtering issue
60+
// TODO: should we reuse the config or potentially have multiple ones with different settings for the same type
6561
private final Map<Class<?>, AotTypeConfiguration> typeConfigurations = new HashMap<>();
66-
private final Environment environment;
62+
private final Environment environment;
6763

6864
public DefaultAotContext(BeanFactory beanFactory, Environment environment) {
65+
this(beanFactory, environment, new AotMappingContext());
66+
}
67+
68+
DefaultAotContext(BeanFactory beanFactory, Environment environment, AotMappingContext mappingContext) {
6969
this.factory = beanFactory instanceof ConfigurableListableBeanFactory cbf ? cbf
7070
: new DefaultListableBeanFactory(beanFactory);
7171
this.environment = environment;
72+
this.mappingContext = mappingContext;
7273
}
7374

7475
@Override
@@ -188,7 +189,6 @@ class ContextualTypeConfiguration implements AotTypeConfiguration {
188189
private boolean contributeAccessors = false;
189190
private boolean forQuerydsl = false;
190191
private final List<List<TypeReference>> proxies = new ArrayList<>();
191-
private Predicate<Class<?>> filter;
192192

193193
ContextualTypeConfiguration(Class<?> type) {
194194
this.type = type;
@@ -224,20 +224,9 @@ public AotTypeConfiguration forQuerydsl() {
224224
return this;
225225
}
226226

227-
@Override
228-
public AotTypeConfiguration filter(Predicate<Class<?>> filter) {
229-
230-
this.filter = filter;
231-
return this;
232-
}
233-
234227
@Override
235228
public void contribute(Environment environment, GenerationContext generationContext) {
236229

237-
if (filter != null && !filter.test(this.type)) {
238-
return;
239-
}
240-
241230
if (!this.categories.isEmpty()) {
242231
generationContext.getRuntimeHints().reflection().registerType(this.type,
243232
categories.toArray(MemberCategory[]::new));
@@ -255,11 +244,7 @@ public void contribute(Environment environment, GenerationContext generationCont
255244
}
256245

257246
if (forDataBinding) {
258-
259247
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);
263248
}
264249

265250
if (forQuerydsl) {
@@ -268,9 +253,8 @@ public void contribute(Environment environment, GenerationContext generationCont
268253

269254
if (!proxies.isEmpty()) {
270255
for (List<TypeReference> proxyInterfaces : proxies) {
271-
generationContext.getRuntimeHints().proxies()
272-
.registerJdkProxy(Stream.concat(Stream.of(TypeReference.of(type)), proxyInterfaces.stream())
273-
.toArray(TypeReference[]::new));
256+
generationContext.getRuntimeHints().proxies().registerJdkProxy(
257+
Stream.concat(Stream.of(TypeReference.of(type)), proxyInterfaces.stream()).toArray(TypeReference[]::new));
274258
}
275259
}
276260

0 commit comments

Comments
 (0)