Skip to content

Commit d38b551

Browse files
committed
Refine AOT composition detection.
Associate Repository Bean Definition with RepositoryConfiguration and RepositoryConfigurationExtension attributes to capture configuration details such as the module name or the configuration source. Introduce RepositoryFragmentsContributor to provide an abstraction for structural fragment implementation allowing to describe the implementation type instead of requiring the implementation object. Obtain repository fragments from a RepositoryFragmentsContributor (either the configured one or one from a RepositoryFactoryBean). Closes: #3279 Original Pull Request: #3282
1 parent ad1ba53 commit d38b551

36 files changed

+913
-356
lines changed

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

+73-30
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.javapoet.ClassName;
4242
import org.springframework.javapoet.FieldSpec;
4343
import org.springframework.javapoet.JavaFile;
44+
import org.springframework.javapoet.MethodSpec;
4445
import org.springframework.javapoet.TypeName;
4546
import org.springframework.javapoet.TypeSpec;
4647

@@ -53,16 +54,19 @@
5354
class AotRepositoryBuilder {
5455

5556
private final RepositoryInformation repositoryInformation;
57+
private final String moduleName;
5658
private final ProjectionFactory projectionFactory;
5759
private final AotRepositoryFragmentMetadata generationMetadata;
5860

5961
private @Nullable Consumer<AotRepositoryConstructorBuilder> constructorCustomizer;
6062
private @Nullable BiFunction<Method, RepositoryInformation, @Nullable MethodContributor<? extends QueryMethod>> methodContributorFunction;
6163
private ClassCustomizer customizer;
6264

63-
private AotRepositoryBuilder(RepositoryInformation repositoryInformation, ProjectionFactory projectionFactory) {
65+
private AotRepositoryBuilder(RepositoryInformation repositoryInformation, String moduleName,
66+
ProjectionFactory projectionFactory) {
6467

6568
this.repositoryInformation = repositoryInformation;
69+
this.moduleName = moduleName;
6670
this.projectionFactory = projectionFactory;
6771

6872
this.generationMetadata = new AotRepositoryFragmentMetadata(className());
@@ -74,54 +78,71 @@ private AotRepositoryBuilder(RepositoryInformation repositoryInformation, Projec
7478
this.customizer = (info, metadata, builder) -> {};
7579
}
7680

77-
public static <M extends QueryMethod> AotRepositoryBuilder forRepository(RepositoryInformation repositoryInformation,
81+
/**
82+
* Create a new {@code AotRepositoryBuilder} for the given {@link RepositoryInformation}.
83+
*
84+
* @param information must not be {@literal null}.
85+
* @param moduleName must not be {@literal null}.
86+
* @param projectionFactory must not be {@literal null}.
87+
* @return
88+
*/
89+
public static AotRepositoryBuilder forRepository(RepositoryInformation information, String moduleName,
7890
ProjectionFactory projectionFactory) {
79-
return new AotRepositoryBuilder(repositoryInformation, projectionFactory);
91+
return new AotRepositoryBuilder(information, moduleName, projectionFactory);
8092
}
8193

94+
/**
95+
* Configure a {@link ClassCustomizer} customizer.
96+
*
97+
* @param classCustomizer must not be {@literal null}.
98+
* @return {@code this}.
99+
*/
100+
public AotRepositoryBuilder withClassCustomizer(ClassCustomizer classCustomizer) {
101+
102+
this.customizer = classCustomizer;
103+
return this;
104+
}
105+
106+
/**
107+
* Configure a {@link AotRepositoryConstructorBuilder} customizer.
108+
*
109+
* @param constructorCustomizer must not be {@literal null}.
110+
* @return {@code this}.
111+
*/
82112
public AotRepositoryBuilder withConstructorCustomizer(
83113
Consumer<AotRepositoryConstructorBuilder> constructorCustomizer) {
84114

85115
this.constructorCustomizer = constructorCustomizer;
86116
return this;
87117
}
88118

119+
/**
120+
* Configure a {@link MethodContributor}.
121+
*
122+
* @param methodContributorFunction must not be {@literal null}.
123+
* @return {@code this}.
124+
*/
89125
public AotRepositoryBuilder withQueryMethodContributor(
90126
BiFunction<Method, RepositoryInformation, @Nullable MethodContributor<? extends QueryMethod>> methodContributorFunction) {
91-
this.methodContributorFunction = methodContributorFunction;
92-
return this;
93-
}
94127

95-
public AotRepositoryBuilder withClassCustomizer(ClassCustomizer classCustomizer) {
96-
97-
this.customizer = classCustomizer;
128+
this.methodContributorFunction = methodContributorFunction;
98129
return this;
99130
}
100131

101132
public AotBundle build() {
102133

134+
List<AotRepositoryMethod> methodMetadata = new ArrayList<>();
135+
RepositoryComposition repositoryComposition = repositoryInformation.getRepositoryComposition();
136+
103137
// start creating the type
104138
TypeSpec.Builder builder = TypeSpec.classBuilder(this.generationMetadata.getTargetTypeName()) //
105139
.addModifiers(Modifier.PUBLIC) //
106140
.addAnnotation(Generated.class) //
107-
.addJavadoc("AOT generated repository implementation for {@link $T}.\n",
141+
.addJavadoc("AOT generated $L repository implementation for {@link $T}.\n", moduleName,
108142
repositoryInformation.getRepositoryInterface());
109143

110144
// create the constructor
111-
AotRepositoryConstructorBuilder constructorBuilder = new AotRepositoryConstructorBuilder(repositoryInformation,
112-
generationMetadata);
113-
if (constructorCustomizer != null) {
114-
constructorCustomizer.accept(constructorBuilder);
115-
}
116-
117-
builder.addMethod(constructorBuilder.buildConstructor());
118-
119-
List<AotRepositoryMethod> methodMetadata = new ArrayList<>();
120-
AotRepositoryMetadata.RepositoryType repositoryType = repositoryInformation.isReactiveRepository()
121-
? AotRepositoryMetadata.RepositoryType.REACTIVE
122-
: AotRepositoryMetadata.RepositoryType.IMPERATIVE;
123-
124-
RepositoryComposition repositoryComposition = repositoryInformation.getRepositoryComposition();
145+
builder.addMethod(buildConstructor());
125146

126147
Arrays.stream(repositoryInformation.getRepositoryInterface().getMethods())
127148
.sorted(Comparator.<Method, String> comparing(it -> {
@@ -136,12 +157,35 @@ public AotBundle build() {
136157

137158
// finally customize the file itself
138159
this.customizer.customize(repositoryInformation, generationMetadata, builder);
160+
139161
JavaFile javaFile = JavaFile.builder(packageName(), builder.build()).build();
162+
AotRepositoryMetadata metadata = getAotRepositoryMetadata(methodMetadata);
163+
164+
return new AotBundle(javaFile, metadata);
165+
}
166+
167+
private MethodSpec buildConstructor() {
168+
169+
AotRepositoryConstructorBuilder constructorBuilder = new AotRepositoryConstructorBuilder(repositoryInformation,
170+
generationMetadata);
171+
172+
if (constructorCustomizer != null) {
173+
constructorCustomizer.accept(constructorBuilder);
174+
}
175+
176+
return constructorBuilder.buildConstructor();
177+
}
140178

141-
AotRepositoryMetadata metadata = new AotRepositoryMetadata(repositoryInformation.getRepositoryInterface().getName(),
142-
repositoryInformation.moduleName() != null ? repositoryInformation.moduleName() : "", repositoryType, methodMetadata);
179+
private AotRepositoryMetadata getAotRepositoryMetadata(List<AotRepositoryMethod> methodMetadata) {
143180

144-
return new AotBundle(javaFile, metadata.toJson());
181+
AotRepositoryMetadata.RepositoryType repositoryType = repositoryInformation.isReactiveRepository()
182+
? AotRepositoryMetadata.RepositoryType.REACTIVE
183+
: AotRepositoryMetadata.RepositoryType.IMPERATIVE;
184+
185+
String jsonModuleName = moduleName.replaceAll("Reactive", "").trim();
186+
187+
return new AotRepositoryMetadata(repositoryInformation.getRepositoryInterface().getName(), jsonModuleName,
188+
repositoryType, methodMetadata);
145189
}
146190

147191
private void contributeMethod(Method method, RepositoryComposition repositoryComposition,
@@ -185,8 +229,7 @@ private void contributeMethod(Method method, RepositoryComposition repositoryCom
185229
private AotRepositoryMethod getFragmentMetadata(Method method, RepositoryFragment<?> fragment) {
186230

187231
String signature = fragment.getSignatureContributor().getName();
188-
String implementation = fragment.getImplementation().map(it -> it.getClass().getName()).orElse(null);
189-
232+
String implementation = fragment.getImplementationClass().map(Class::getName).orElse(null);
190233
AotFragmentTarget fragmentTarget = new AotFragmentTarget(signature, implementation);
191234

192235
return new AotRepositoryMethod(method.getName(), method.toGenericString(), null, fragmentTarget);
@@ -240,7 +283,7 @@ public interface ClassCustomizer {
240283

241284
}
242285

243-
record AotBundle(JavaFile javaFile, JSONObject metadata) {
286+
record AotBundle(JavaFile javaFile, AotRepositoryMetadata metadata) {
244287
}
245288

246289
}

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

+22-9
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,42 @@
3939
*
4040
* @author Christoph Strobl
4141
* @author Mark Paluch
42+
* @since 4.0
4243
*/
4344
public class RepositoryContributor {
4445

4546
private static final Log logger = LogFactory.getLog(RepositoryContributor.class);
4647

4748
private final AotRepositoryBuilder builder;
4849

50+
/**
51+
* Create a new {@code RepositoryContributor} for the given {@link AotRepositoryContext}.
52+
*
53+
* @param repositoryContext
54+
*/
4955
public RepositoryContributor(AotRepositoryContext repositoryContext) {
5056
this.builder = AotRepositoryBuilder.forRepository(repositoryContext.getRepositoryInformation(),
51-
createProjectionFactory());
57+
repositoryContext.getModuleName(), createProjectionFactory());
5258
}
5359

60+
/**
61+
* @return a new {@link ProjectionFactory} to be used with the AOT repository builder. The actual instance should be
62+
* accessed through {@link #getProjectionFactory()}.
63+
*/
5464
protected ProjectionFactory createProjectionFactory() {
5565
return new SpelAwareProxyProjectionFactory();
5666
}
5767

68+
/**
69+
* @return the used {@link ProjectionFactory}.
70+
*/
5871
protected ProjectionFactory getProjectionFactory() {
5972
return builder.getProjectionFactory();
6073
}
6174

75+
/**
76+
* @return the used {@link RepositoryInformation}.
77+
*/
6278
protected RepositoryInformation getRepositoryInformation() {
6379
return builder.getRepositoryInformation();
6480
}
@@ -73,13 +89,10 @@ public java.util.Map<String, TypeName> requiredArgs() {
7389

7490
public void contribute(GenerationContext generationContext) {
7591

76-
// TODO: do we need - generationContext.withName("spring-data");
77-
78-
builder.withClassCustomizer(this::customizeClass);
79-
builder.withConstructorCustomizer(this::customizeConstructor);
80-
builder.withQueryMethodContributor(this::contributeQueryMethod);
81-
82-
AotRepositoryBuilder.AotBundle aotBundle = builder.build();
92+
AotRepositoryBuilder.AotBundle aotBundle = builder.withClassCustomizer(this::customizeClass) //
93+
.withConstructorCustomizer(this::customizeConstructor) //
94+
.withQueryMethodContributor(this::contributeQueryMethod) //
95+
.build();
8396

8497
Class<?> repositoryInterface = getRepositoryInformation().getRepositoryInterface();
8598
String repositoryJsonFileName = getRepositoryJsonFileName(repositoryInterface);
@@ -89,7 +102,7 @@ public void contribute(GenerationContext generationContext) {
89102
String repositoryJson;
90103

91104
try {
92-
repositoryJson = aotBundle.metadata().toString(2);
105+
repositoryJson = aotBundle.metadata().toJson().toString(2);
93106
} catch (JSONException e) {
94107
throw new RuntimeException(e);
95108
}

src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java

+11
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
6565
private static final String QUERY_LOOKUP_STRATEGY = "queryLookupStrategy";
6666
private static final String REPOSITORY_FACTORY_BEAN_CLASS = "repositoryFactoryBeanClass";
6767
private static final String REPOSITORY_BASE_CLASS = "repositoryBaseClass";
68+
private static final String REPOSITORY_FRAGMENTS_CONTRIBUTOR_CLASS = "fragmentsContributor";
6869
private static final String CONSIDER_NESTED_REPOSITORIES = "considerNestedRepositories";
6970
private static final String BOOTSTRAP_MODE = "bootstrapMode";
7071
private static final String BEAN_NAME_GENERATOR = "nameGenerator";
@@ -187,6 +188,16 @@ public Optional<String> getRepositoryBaseClassName() {
187188
: Optional.of(repositoryBaseClass.getName());
188189
}
189190

191+
@Override
192+
public Optional<String> getRepositoryFragmentsContributorClassName() {
193+
194+
if (!attributes.containsKey(REPOSITORY_FRAGMENTS_CONTRIBUTOR_CLASS)) {
195+
return Optional.empty();
196+
}
197+
198+
return Optional.of(attributes.getClass(REPOSITORY_FRAGMENTS_CONTRIBUTOR_CLASS).getName());
199+
}
200+
190201
/**
191202
* Returns the {@link AnnotationAttributes} of the annotation configured.
192203
*

src/main/java/org/springframework/data/repository/config/AotRepositoryContext.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
package org.springframework.data.repository.config;
1717

1818
import java.lang.annotation.Annotation;
19+
import java.util.Collection;
1920
import java.util.Set;
2021

21-
import org.springframework.core.SpringProperties;
2222
import org.springframework.core.annotation.MergedAnnotation;
2323
import org.springframework.data.aot.AotContext;
2424
import org.springframework.data.repository.core.RepositoryInformation;
@@ -28,8 +28,9 @@
2828
*
2929
* @author Christoph Strobl
3030
* @author John Blum
31-
* @see AotContext
31+
* @author Mark Paluch
3232
* @since 3.0
33+
* @see AotContext
3334
*/
3435
public interface AotRepositoryContext extends AotContext {
3536

@@ -38,6 +39,12 @@ public interface AotRepositoryContext extends AotContext {
3839
*/
3940
String getBeanName();
4041

42+
/**
43+
* @return the Spring Data module name, see {@link RepositoryConfigurationExtension#getModuleName()}.
44+
* @since 4.0
45+
*/
46+
String getModuleName();
47+
4148
/**
4249
* @return a {@link Set} of {@link String base packages} to search for repositories.
4350
*/
@@ -46,7 +53,7 @@ public interface AotRepositoryContext extends AotContext {
4653
/**
4754
* @return the {@link Annotation} types used to identify domain types.
4855
*/
49-
Set<Class<? extends Annotation>> getIdentifyingAnnotations();
56+
Collection<Class<? extends Annotation>> getIdentifyingAnnotations();
5057

5158
/**
5259
* @return {@link RepositoryInformation metadata} about the repository itself.
@@ -64,4 +71,5 @@ public interface AotRepositoryContext extends AotContext {
6471
* @return all {@link Class types} reachable from the repository.
6572
*/
6673
Set<Class<?>> getResolvedTypes();
74+
6775
}

0 commit comments

Comments
 (0)