Skip to content

Commit cebd2c4

Browse files
committed
Polishing.
Introduce Environment to AotContext. Move JSON classes to aot/generate package. Refine exposed API. See #3265
1 parent 7a40f71 commit cebd2c4

28 files changed

+290
-288
lines changed

Diff for: src/main/java/org/springframework/data/aot/AotContext.java

+22-7
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import org.springframework.beans.factory.config.BeanReference;
3131
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3232
import org.springframework.beans.factory.support.RootBeanDefinition;
33-
import org.springframework.core.SpringProperties;
33+
import org.springframework.core.env.Environment;
34+
import org.springframework.core.env.EnvironmentCapable;
35+
import org.springframework.core.env.StandardEnvironment;
3436
import org.springframework.data.util.TypeScanner;
3537
import org.springframework.util.Assert;
3638

@@ -45,29 +47,42 @@
4547
* @author Christoph Strobl
4648
* @author John Blum
4749
* @author Mark Paluch
48-
* @see BeanFactory
4950
* @since 3.0
51+
* @see BeanFactory
5052
*/
51-
public interface AotContext {
53+
public interface AotContext extends EnvironmentCapable {
5254

5355
String GENERATED_REPOSITORIES_ENABLED = "spring.aot.repositories.enabled";
5456

55-
static boolean aotGeneratedRepositoriesEnabled() {
56-
return SpringProperties.getFlag(GENERATED_REPOSITORIES_ENABLED);
57+
/**
58+
* Create an {@link AotContext} backed by the given {@link BeanFactory}.
59+
*
60+
* @param beanFactory reference to the {@link BeanFactory}; must not be {@literal null}.
61+
* @return a new instance of {@link AotContext}.
62+
* @see BeanFactory
63+
*/
64+
static AotContext from(BeanFactory beanFactory) {
65+
66+
Assert.notNull(beanFactory, "BeanFactory must not be null");
67+
68+
return new DefaultAotContext(beanFactory, new StandardEnvironment());
5769
}
5870

5971
/**
6072
* Create an {@link AotContext} backed by the given {@link BeanFactory}.
6173
*
6274
* @param beanFactory reference to the {@link BeanFactory}; must not be {@literal null}.
6375
* @return a new instance of {@link AotContext}.
76+
* @param environment reference to the {@link Environment}; must not be {@literal null}.
77+
* @return a new instance of {@link AotContext}.
6478
* @see BeanFactory
6579
*/
66-
static AotContext from(BeanFactory beanFactory) {
80+
static AotContext from(BeanFactory beanFactory, Environment environment) {
6781

6882
Assert.notNull(beanFactory, "BeanFactory must not be null");
83+
Assert.notNull(environment, "Environment must not be null");
6984

70-
return new DefaultAotContext(beanFactory);
85+
return new DefaultAotContext(beanFactory, environment);
7186
}
7287

7388
/**

Diff for: src/main/java/org/springframework/data/aot/DefaultAotContext.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2929
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3030
import org.springframework.beans.factory.support.RootBeanDefinition;
31+
import org.springframework.core.env.Environment;
3132
import org.springframework.util.ClassUtils;
3233

3334
/**
@@ -40,16 +41,24 @@ class DefaultAotContext implements AotContext {
4041

4142
private final ConfigurableListableBeanFactory factory;
4243

43-
public DefaultAotContext(BeanFactory beanFactory) {
44+
private final Environment environment;
45+
46+
public DefaultAotContext(BeanFactory beanFactory, Environment environment) {
4447
factory = beanFactory instanceof ConfigurableListableBeanFactory cbf ? cbf
4548
: new DefaultListableBeanFactory(beanFactory);
49+
this.environment = environment;
4650
}
4751

4852
@Override
4953
public ConfigurableListableBeanFactory getBeanFactory() {
5054
return factory;
5155
}
5256

57+
@Override
58+
public Environment getEnvironment() {
59+
return environment;
60+
}
61+
5362
@Override
5463
public TypeIntrospector introspectType(String typeName) {
5564
return new DefaultTypeIntrospector(typeName);
@@ -138,4 +147,5 @@ public RootBeanDefinition getRootBeanDefinition() throws NoSuchBeanDefinitionExc
138147
return factory.getType(beanName, false);
139148
}
140149
}
150+
141151
}

Diff for: src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@
3131
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
3232
import org.springframework.beans.factory.support.RegisteredBean;
3333
import org.springframework.beans.factory.support.RootBeanDefinition;
34+
import org.springframework.context.EnvironmentAware;
3435
import org.springframework.core.ResolvableType;
36+
import org.springframework.core.env.Environment;
37+
import org.springframework.core.env.StandardEnvironment;
3538
import org.springframework.data.domain.ManagedTypes;
39+
import org.springframework.data.util.Lazy;
3640
import org.springframework.data.util.QTypeContributor;
3741
import org.springframework.data.util.TypeContributor;
3842
import org.springframework.data.util.TypeUtils;
@@ -47,10 +51,11 @@
4751
* @author John Blum
4852
* @since 3.0
4953
*/
50-
public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
54+
public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor, EnvironmentAware {
5155

5256
private final Log logger = LogFactory.getLog(getClass());
5357
private @Nullable String moduleIdentifier;
58+
private Lazy<Environment> environment = Lazy.of(StandardEnvironment::new);
5459

5560
public void setModuleIdentifier(@Nullable String moduleIdentifier) {
5661
this.moduleIdentifier = moduleIdentifier;
@@ -61,6 +66,11 @@ public String getModuleIdentifier() {
6166
return this.moduleIdentifier;
6267
}
6368

69+
@Override
70+
public void setEnvironment(Environment environment) {
71+
this.environment = Lazy.of(() -> environment);
72+
}
73+
6474
@Override
6575
public @Nullable BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
6676

@@ -69,7 +79,8 @@ public String getModuleIdentifier() {
6979
}
7080

7181
BeanFactory beanFactory = registeredBean.getBeanFactory();
72-
return contribute(AotContext.from(beanFactory), resolveManagedTypes(registeredBean), registeredBean);
82+
return contribute(AotContext.from(beanFactory, this.environment.get()), resolveManagedTypes(registeredBean),
83+
registeredBean);
7384
}
7485

7586
private ManagedTypes resolveManagedTypes(RegisteredBean registeredBean) {

Diff for: src/main/java/org/springframework/data/repository/aot/generate/AotFragmentTarget.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717

1818
import org.jspecify.annotations.Nullable;
1919

20-
import org.springframework.data.repository.aot.generate.json.JSONException;
21-
import org.springframework.data.repository.aot.generate.json.JSONObject;
22-
2320
/**
21+
* Value object capturing metadata about a fragment target.
22+
*
2423
* @author Mark Paluch
2524
* @since 4.0
2625
*/
@@ -39,4 +38,5 @@ public JSONObject toJson() throws JSONException {
3938

4039
return fragment;
4140
}
41+
4242
}

Diff for: src/main/java/org/springframework/data/repository/aot/generate/AotQueryMethodGenerationContext.java

+4-20
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,11 @@
1-
/*
2-
* Copyright 2025. the original author or authors.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
16-
171
/*
182
* Copyright 2025 the original author or authors.
193
*
204
* Licensed under the Apache License, Version 2.0 (the "License");
215
* you may not use this file except in compliance with the License.
226
* You may obtain a copy of the License at
237
*
24-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
259
*
2610
* Unless required by applicable law or agreed to in writing, software
2711
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -68,7 +52,7 @@ public class AotQueryMethodGenerationContext {
6852
private final QueryMethod queryMethod;
6953
private final RepositoryInformation repositoryInformation;
7054
private final AotRepositoryFragmentMetadata targetTypeMetadata;
71-
private final AotRepositoryMethodImplementationMetadata targetMethodMetadata;
55+
private final MethodMetadata targetMethodMetadata;
7256
private final CodeBlocks codeBlocks;
7357

7458
AotQueryMethodGenerationContext(RepositoryInformation repositoryInformation, Method method, QueryMethod queryMethod,
@@ -79,15 +63,15 @@ public class AotQueryMethodGenerationContext {
7963
this.queryMethod = queryMethod;
8064
this.repositoryInformation = repositoryInformation;
8165
this.targetTypeMetadata = targetTypeMetadata;
82-
this.targetMethodMetadata = new AotRepositoryMethodImplementationMetadata(repositoryInformation, method);
66+
this.targetMethodMetadata = new MethodMetadata(repositoryInformation, method);
8367
this.codeBlocks = new CodeBlocks(targetTypeMetadata);
8468
}
8569

8670
AotRepositoryFragmentMetadata getTargetTypeMetadata() {
8771
return targetTypeMetadata;
8872
}
8973

90-
AotRepositoryMethodImplementationMetadata getTargetMethodMetadata() {
74+
MethodMetadata getTargetMethodMetadata() {
9175
return targetMethodMetadata;
9276
}
9377

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

+4-10
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
import org.springframework.aot.generate.Generated;
3535
import org.springframework.data.projection.ProjectionFactory;
3636
import org.springframework.data.repository.aot.generate.AotRepositoryFragmentMetadata.ConstructorArgument;
37-
import org.springframework.data.repository.aot.generate.json.JSONException;
38-
import org.springframework.data.repository.aot.generate.json.JSONObject;
3937
import org.springframework.data.repository.core.RepositoryInformation;
4038
import org.springframework.data.repository.core.support.RepositoryComposition;
4139
import org.springframework.data.repository.core.support.RepositoryFragment;
@@ -59,7 +57,7 @@ class AotRepositoryBuilder {
5957
private final AotRepositoryFragmentMetadata generationMetadata;
6058

6159
private @Nullable Consumer<AotRepositoryConstructorBuilder> constructorCustomizer;
62-
private @Nullable BiFunction<Method, RepositoryInformation, MethodContributor<? extends QueryMethod>> methodContributorFunction;
60+
private @Nullable BiFunction<Method, RepositoryInformation, @Nullable MethodContributor<? extends QueryMethod>> methodContributorFunction;
6361
private ClassCustomizer customizer;
6462

6563
private AotRepositoryBuilder(RepositoryInformation repositoryInformation, ProjectionFactory projectionFactory) {
@@ -89,7 +87,7 @@ public AotRepositoryBuilder withConstructorCustomizer(
8987
}
9088

9189
public AotRepositoryBuilder withQueryMethodContributor(
92-
BiFunction<Method, RepositoryInformation, MethodContributor<? extends QueryMethod>> methodContributorFunction) {
90+
BiFunction<Method, RepositoryInformation, @Nullable MethodContributor<? extends QueryMethod>> methodContributorFunction) {
9391
this.methodContributorFunction = methodContributorFunction;
9492
return this;
9593
}
@@ -144,11 +142,7 @@ public AotBundle build() {
144142
AotRepositoryMetadata metadata = new AotRepositoryMetadata(repositoryInformation.getRepositoryInterface().getName(),
145143
"", repositoryType, methodMetadata);
146144

147-
try {
148-
return new AotBundle(javaFile, metadata.toJson());
149-
} catch (JSONException e) {
150-
throw new IllegalStateException(e);
151-
}
145+
return new AotBundle(javaFile, metadata.toJson());
152146
}
153147

154148
private void contributeMethod(Method method, RepositoryComposition repositoryComposition,
@@ -218,7 +212,7 @@ private String typeName() {
218212
public Map<String, TypeName> getAutowireFields() {
219213
Map<String, TypeName> autowireFields = new LinkedHashMap<>(generationMetadata.getConstructorArguments().size());
220214
for (Map.Entry<String, ConstructorArgument> entry : generationMetadata.getConstructorArguments().entrySet()) {
221-
autowireFields.put(entry.getKey(), entry.getValue().getTypeName());
215+
autowireFields.put(entry.getKey(), entry.getValue().typeName());
222216
}
223217
return autowireFields;
224218
}

Diff for: src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryConstructorBuilder.java

+2-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.data.repository.aot.generate;
1717

18-
import java.util.List;
1918
import java.util.Map.Entry;
2019

2120
import javax.lang.model.element.Modifier;
@@ -34,6 +33,7 @@
3433
* @author Mark Paluch
3534
* @since 4.0
3635
*/
36+
// TODO: extract constructor contributor in a similar way to MethodContributor.
3737
public class AotRepositoryConstructorBuilder {
3838

3939
private final RepositoryInformation repositoryInformation;
@@ -103,7 +103,7 @@ MethodSpec buildConstructor() {
103103
MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);
104104

105105
for (Entry<String, ConstructorArgument> parameter : this.metadata.getConstructorArguments().entrySet()) {
106-
builder.addParameter(parameter.getValue().getTypeName(), parameter.getKey());
106+
builder.addParameter(parameter.getValue().typeName(), parameter.getKey());
107107
}
108108

109109
customizer.customize(repositoryInformation, builder);
@@ -118,19 +118,6 @@ MethodSpec buildConstructor() {
118118
return builder.build();
119119
}
120120

121-
private static TypeName getDefaultStoreRepositoryImplementationType(RepositoryInformation repositoryInformation) {
122-
123-
ResolvableType resolvableType = ResolvableType.forClass(repositoryInformation.getRepositoryBaseClass());
124-
if (resolvableType.hasGenerics()) {
125-
List<Class<?>> generics = List.of();
126-
if (resolvableType.getGenerics().length == 2) { // TODO: Find some other way to resolve generics
127-
generics = List.of(repositoryInformation.getDomainType(), repositoryInformation.getIdType());
128-
}
129-
return ParameterizedTypeName.get(repositoryInformation.getRepositoryBaseClass(), generics.toArray(Class[]::new));
130-
}
131-
return TypeName.get(repositoryInformation.getRepositoryBaseClass());
132-
}
133-
134121
/**
135122
* Customizer for the AOT repository constructor.
136123
*/

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

+2-21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
/**
3232
* @author Christoph Strobl
3333
*/
34+
// TODO: Can we make this package-private?
3435
public class AotRepositoryFragmentMetadata {
3536

3637
private final ClassName className;
@@ -94,31 +95,11 @@ public void addConstructorArgument(String parameterName, TypeName type, @Nullabl
9495
this.constructorArguments.put(parameterName, new ConstructorArgument(parameterName, type, fieldName));
9596
}
9697

97-
static class ConstructorArgument {
98-
String parameterName;
99-
@Nullable String fieldName;
100-
TypeName typeName;
101-
102-
public ConstructorArgument(String parameterName,TypeName typeName, String fieldName) {
103-
this.parameterName = parameterName;
104-
this.fieldName = fieldName;
105-
this.typeName = typeName;
106-
}
98+
public record ConstructorArgument(String parameterName, TypeName typeName, @Nullable String fieldName) {
10799

108100
boolean isForLocalField() {
109101
return fieldName != null;
110102
}
111103

112-
public String getParameterName() {
113-
return parameterName;
114-
}
115-
116-
public String getFieldName() {
117-
return fieldName;
118-
}
119-
120-
public TypeName getTypeName() {
121-
return typeName;
122-
}
123104
}
124105
}

Diff for: src/main/java/org/springframework/data/repository/aot/generate/AotRepositoryMetadata.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717

1818
import java.util.List;
1919

20-
import org.springframework.data.repository.aot.generate.json.JSONArray;
21-
import org.springframework.data.repository.aot.generate.json.JSONException;
22-
import org.springframework.data.repository.aot.generate.json.JSONObject;
23-
2420
/**
21+
* Value object capturing metadata about a repository.
22+
*
2523
* @author Mark Paluch
2624
* @since 4.0
2725
*/
@@ -33,6 +31,12 @@ enum RepositoryType {
3331
IMPERATIVE, REACTIVE
3432
}
3533

34+
/**
35+
* Convert this {@link AotRepositoryMetadata} to a {@link JSONObject}.
36+
*
37+
* @return
38+
* @throws JSONException
39+
*/
3640
JSONObject toJson() throws JSONException {
3741

3842
JSONObject metadata = new JSONObject();
@@ -49,6 +53,6 @@ JSONObject toJson() throws JSONException {
4953
metadata.put("methods", methods);
5054

5155
return metadata;
52-
5356
}
57+
5458
}

0 commit comments

Comments
 (0)