Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit dc5483a

Browse files
christophstroblmp911de
authored andcommitted
Use generated classname for writing aot repository content.
This change makes sure register AOT repository code with a generated typename. This is necessary to allow recreation for repository code with different configuration/context settings during tests. Closes #3339 Original pull request: #3345
1 parent 370cb37 commit dc5483a

File tree

6 files changed

+140
-86
lines changed

6 files changed

+140
-86
lines changed

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

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,16 @@
2929
import org.apache.commons.logging.Log;
3030
import org.apache.commons.logging.LogFactory;
3131
import org.jspecify.annotations.Nullable;
32-
33-
import org.springframework.aot.generate.ClassNameGenerator;
3432
import org.springframework.aot.generate.Generated;
33+
import org.springframework.aot.generate.GeneratedTypeReference;
34+
import org.springframework.aot.hint.TypeReference;
3535
import org.springframework.data.projection.ProjectionFactory;
3636
import org.springframework.data.repository.aot.generate.AotRepositoryFragmentMetadata.ConstructorArgument;
3737
import org.springframework.data.repository.core.RepositoryInformation;
3838
import org.springframework.data.repository.core.support.RepositoryComposition;
3939
import org.springframework.data.repository.core.support.RepositoryFragment;
4040
import org.springframework.data.repository.query.QueryMethod;
4141
import org.springframework.javapoet.ClassName;
42-
import org.springframework.javapoet.FieldSpec;
4342
import org.springframework.javapoet.JavaFile;
4443
import org.springframework.javapoet.MethodSpec;
4544
import org.springframework.javapoet.TypeName;
@@ -64,6 +63,8 @@ class AotRepositoryBuilder {
6463
private @Nullable Consumer<AotRepositoryConstructorBuilder> constructorCustomizer;
6564
private @Nullable MethodContributorFactory methodContributorFactory;
6665
private Consumer<AotRepositoryClassBuilder> classCustomizer;
66+
private @Nullable TypeReference targetClassName;
67+
private RepositoryConstructorBuilder constructorBuilder;
6768

6869
private AotRepositoryBuilder(RepositoryInformation repositoryInformation, String moduleName,
6970
ProjectionFactory projectionFactory) {
@@ -72,13 +73,9 @@ private AotRepositoryBuilder(RepositoryInformation repositoryInformation, String
7273
this.moduleName = moduleName;
7374
this.projectionFactory = projectionFactory;
7475

75-
this.generationMetadata = new AotRepositoryFragmentMetadata(className());
76-
this.generationMetadata.addField(FieldSpec
77-
.builder(TypeName.get(Log.class), "logger", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
78-
.initializer("$T.getLog($T.class)", TypeName.get(LogFactory.class), this.generationMetadata.getTargetTypeName())
79-
.build());
80-
76+
this.generationMetadata = new AotRepositoryFragmentMetadata();
8177
this.classCustomizer = (builder) -> {};
78+
this.constructorBuilder = new RepositoryConstructorBuilder(generationMetadata);
8279
}
8380

8481
/**
@@ -131,15 +128,24 @@ public AotRepositoryBuilder withQueryMethodContributor(MethodContributorFactory
131128
return this;
132129
}
133130

134-
public AotBundle build() {
131+
public AotRepositoryBuilder prepare(@Nullable ClassName targetClassName) {
132+
if (targetClassName == null) {
133+
withTargetClassName(null);
134+
} else {
135+
withTargetClassName(GeneratedTypeReference.of(targetClassName));
136+
}
137+
if (constructorCustomizer != null) {
138+
constructorCustomizer.accept(constructorBuilder);
139+
}
140+
return this;
141+
}
142+
143+
public AotBundle build(TypeSpec.Builder builder) {
135144

136145
List<AotRepositoryMethod> methodMetadata = new ArrayList<>();
137146
RepositoryComposition repositoryComposition = repositoryInformation.getRepositoryComposition();
138147

139-
// start creating the type
140-
TypeSpec.Builder builder = TypeSpec.classBuilder(this.generationMetadata.getTargetTypeName()) //
141-
.addModifiers(Modifier.PUBLIC) //
142-
.addAnnotation(Generated.class) //
148+
builder.addModifiers(Modifier.PUBLIC) //
143149
.addJavadoc("AOT generated $L repository implementation for {@link $T}.\n", moduleName,
144150
repositoryInformation.getRepositoryInterface());
145151

@@ -177,15 +183,31 @@ public AotBundle build() {
177183
return new AotBundle(javaFile, metadata);
178184
}
179185

180-
private MethodSpec buildConstructor() {
186+
public AotBundle build() {
187+
188+
ClassName className = ClassName
189+
.bestGuess((targetClassName != null ? targetClassName : intendedTargetClassName()).getCanonicalName());
190+
return build(TypeSpec.classBuilder(className).addAnnotation(Generated.class));
191+
}
181192

182-
RepositoryConstructorBuilder constructorBuilder = new RepositoryConstructorBuilder(
183-
generationMetadata);
193+
public TypeReference intendedTargetClassName() {
194+
return TypeReference.of("%s.%s".formatted(packageName(), typeName()));
195+
}
184196

185-
if (constructorCustomizer != null) {
186-
constructorCustomizer.accept(constructorBuilder);
197+
public @Nullable TypeReference actualTargetClassName() {
198+
199+
if (targetClassName == null) {
200+
return null;
187201
}
202+
return targetClassName;
203+
}
188204

205+
AotRepositoryBuilder withTargetClassName(@Nullable TypeReference targetClassName) {
206+
this.targetClassName = targetClassName;
207+
return this;
208+
}
209+
210+
private MethodSpec buildConstructor() {
189211
return constructorBuilder.buildConstructor();
190212
}
191213

@@ -252,15 +274,11 @@ public AotRepositoryFragmentMetadata getGenerationMetadata() {
252274
return generationMetadata;
253275
}
254276

255-
private ClassName className() {
256-
return new ClassNameGenerator(ClassName.get(packageName(), typeName())).generateClassName("Aot", null);
257-
}
258-
259-
private String packageName() {
277+
public String packageName() {
260278
return repositoryInformation.getRepositoryInterface().getPackageName();
261279
}
262280

263-
private String typeName() {
281+
public String typeName() {
264282
return "%sImpl".formatted(repositoryInformation.getRepositoryInterface().getSimpleName());
265283
}
266284

@@ -280,7 +298,6 @@ public ProjectionFactory getProjectionFactory() {
280298
return projectionFactory;
281299
}
282300

283-
284301
/**
285302
* Customizer interface to customize the AOT repository fragment constructor through
286303
* {@link AotRepositoryConstructorBuilder}.

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,10 @@
3737
*/
3838
public class AotRepositoryFragmentMetadata {
3939

40-
private final ClassName className;
4140
private final Map<String, FieldSpec> fields = new HashMap<>(3);
4241
private final Map<String, ConstructorArgument> constructorArguments = new LinkedHashMap<>(3);
4342

44-
public AotRepositoryFragmentMetadata(ClassName className) {
45-
this.className = className;
43+
public AotRepositoryFragmentMetadata() {
4644
}
4745

4846
/**
@@ -65,10 +63,6 @@ public String fieldNameOf(Class<?> type) {
6563
return null;
6664
}
6765

68-
public ClassName getTargetTypeName() {
69-
return className;
70-
}
71-
7266
/**
7367
* Add a field to the repository fragment.
7468
*

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

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@
2020
import org.apache.commons.logging.Log;
2121
import org.apache.commons.logging.LogFactory;
2222
import org.jspecify.annotations.Nullable;
23-
23+
import org.springframework.aot.generate.GeneratedClass;
24+
import org.springframework.aot.generate.GeneratedTypeReference;
2425
import org.springframework.aot.generate.GenerationContext;
2526
import org.springframework.aot.hint.MemberCategory;
2627
import org.springframework.aot.hint.TypeReference;
2728
import org.springframework.data.projection.ProjectionFactory;
2829
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
30+
import org.springframework.data.repository.aot.generate.AotRepositoryBuilder.AotBundle;
2931
import org.springframework.data.repository.config.AotRepositoryContext;
3032
import org.springframework.data.repository.core.RepositoryInformation;
3133
import org.springframework.data.repository.query.QueryMethod;
32-
import org.springframework.javapoet.JavaFile;
34+
import org.springframework.javapoet.ClassName;
3335
import org.springframework.javapoet.TypeName;
3436

3537
/**
@@ -42,16 +44,21 @@
4244
public class RepositoryContributor {
4345

4446
private static final Log logger = LogFactory.getLog(RepositoryContributor.class);
47+
private static final String FEATURE_NAME = "AotRepository";
4548

46-
private final AotRepositoryBuilder builder;
49+
private AotRepositoryBuilder builder;
50+
private final AotRepositoryContext repositoryContext;
51+
private @Nullable TypeReference contributedTypeName;
4752

4853
/**
4954
* Create a new {@code RepositoryContributor} for the given {@link AotRepositoryContext}.
5055
*
5156
* @param repositoryContext
5257
*/
5358
public RepositoryContributor(AotRepositoryContext repositoryContext) {
54-
this.builder = AotRepositoryBuilder.forRepository(repositoryContext.getRepositoryInformation(),
59+
60+
this.repositoryContext = repositoryContext;
61+
builder = AotRepositoryBuilder.forRepository(repositoryContext.getRepositoryInformation(),
5562
repositoryContext.getModuleName(), createProjectionFactory());
5663
}
5764

@@ -77,8 +84,8 @@ protected RepositoryInformation getRepositoryInformation() {
7784
return builder.getRepositoryInformation();
7885
}
7986

80-
public String getContributedTypeName() {
81-
return builder.getGenerationMetadata().getTargetTypeName().toString();
87+
public @NullableTypeReference getContributedTypeName() {
88+
return this.contributedTypeName;
8289
}
8390

8491
public java.util.Map<String, TypeName> requiredArgs() {
@@ -87,44 +94,47 @@ public java.util.Map<String, TypeName> requiredArgs() {
8794

8895
public void contribute(GenerationContext generationContext) {
8996

90-
AotRepositoryBuilder.AotBundleaotBundle = builder.withClassCustomizer(this::customizeClass) //
97+
builder.withClassCustomizer(this::customizeClass) //
9198
.withConstructorCustomizer(this::customizeConstructor) //
92-
.withQueryMethodContributor(this::contributeQueryMethod) //
93-
.build();
94-
95-
Class<?> repositoryInterface = getRepositoryInformation().getRepositoryInterface();
96-
String repositoryJsonFileName = getRepositoryJsonFileName(repositoryInterface);
97-
98-
JavaFile javaFile = aotBundle.javaFile();
99-
String typeName = "%s.%s".formatted(javaFile.packageName(), javaFile.typeSpec().name());
100-
String repositoryJson;
101-
102-
try {
103-
repositoryJson = aotBundle.metadata().toJson().toString(2);
104-
} catch (JSONException e) {
105-
throw new RuntimeException(e);
106-
}
107-
108-
if (logger.isTraceEnabled()) {
109-
logger.trace("""
110-
------ AOT Repository.json: %s ------
111-
%s
112-
-------------------
113-
""".formatted(repositoryJsonFileName, repositoryJson));
114-
115-
logger.trace("""
116-
------ AOT Generated Repository: %s ------
117-
%s
118-
-------------------
119-
""".formatted(typeName, javaFile));
120-
}
121-
122-
// generate the files
123-
generationContext.getGeneratedFiles().addSourceFile(javaFile);
124-
generationContext.getGeneratedFiles().addResourceFile(repositoryJsonFileName, repositoryJson);
99+
.withQueryMethodContributor(this::contributeQueryMethod); //
100+
101+
GeneratedClass generatedClass = generationContext.getGeneratedClasses().getOrAddForFeatureComponent(FEATURE_NAME,
102+
ClassName.bestGuess(builder.intendedTargetClassName().getCanonicalName()), targetTypeSpec -> {
103+
104+
AotBundle aotBundle = builder.build(targetTypeSpec);
105+
{
106+
Class<?> repositoryInterface = getRepositoryInformation().getRepositoryInterface();
107+
String repositoryJsonFileName = getRepositoryJsonFileName(repositoryInterface);
108+
String repositoryJson;
109+
try {
110+
repositoryJson = aotBundle.metadata().toJson().toString(2);
111+
} catch (JSONException e) {
112+
throw new RuntimeException(e);
113+
}
114+
115+
if (logger.isTraceEnabled()) {
116+
logger.trace("""
117+
------ AOT Repository.json: %s ------
118+
%s
119+
-------------------
120+
""".formatted(repositoryJsonFileName, repositoryJson));
121+
122+
logger.trace("""
123+
------ AOT Generated Repository: %s ------
124+
%s
125+
-------------------
126+
""".formatted(null, aotBundle.javaFile()));
127+
}
128+
129+
generationContext.getGeneratedFiles().addResourceFile(repositoryJsonFileName, repositoryJson);
130+
}
131+
});
132+
133+
builder.prepare(generatedClass.getName()); // initialize ctor argument resolution and set type name to target
134+
this.contributedTypeName = GeneratedTypeReference.of(generatedClass.getName());
125135

126136
// generate native runtime hints - needed cause we're using the repository proxy
127-
generationContext.getRuntimeHints().reflection().registerType(TypeReference.of(typeName),
137+
generationContext.getRuntimeHints().reflection().registerType(this.contributedTypeName,
128138
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
129139
}
130140

‎src/main/java/org/springframework/data/repository/config/AotRepositoryBeanDefinitionPropertiesDecorator.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
2525
import org.springframework.javapoet.CodeBlock;
2626
import org.springframework.javapoet.TypeName;
27+
import org.springframework.util.Assert;
2728
import org.springframework.util.StringUtils;
2829

2930
/**
@@ -51,6 +52,8 @@ public AotRepositoryBeanDefinitionPropertiesDecorator(Supplier<CodeBlock> inheri
5152
*/
5253
public CodeBlock decorate() {
5354

55+
Assert.notNull(repositoryContributor.getContributedTypeName(), "contributed type name must not be null");
56+
5457
CodeBlock.Builder builder = CodeBlock.builder();
5558
// bring in properties as usual
5659
builder.add(inheritedProperties.get());
@@ -78,7 +81,7 @@ public CodeBlock decorate() {
7881
}
7982

8083
builder.addStatement("return RepositoryComposition.RepositoryFragments.just(new $L($L))",
81-
repositoryContributor.getContributedTypeName(),
84+
repositoryContributor.getContributedTypeName().getCanonicalName(),
8285
StringUtils.collectionToDelimitedString(repositoryContributor.requiredArgs().keySet(), ", "));
8386
builder.unindent();
8487
builder.add("}\n");

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /