Skip to content

Commit 4a48edc

Browse files
Move method parameter initialization to MethodMetadata.
1 parent 7b5f476 commit 4a48edc

File tree

5 files changed

+202
-52
lines changed

5 files changed

+202
-52
lines changed

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

+5-22
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.lang.reflect.Method;
2020
import java.util.ArrayList;
2121
import java.util.List;
22-
import java.util.Map.Entry;
2322

2423
import javax.lang.model.element.Modifier;
2524

@@ -64,8 +63,8 @@ public class AotQueryMethodGenerationContext {
6463
this.repositoryInformation = repositoryInformation;
6564
this.targetTypeMetadata = targetTypeMetadata;
6665
this.targetMethodMetadata = new MethodMetadata(repositoryInformation, method);
67-
this.codeBlocks = new CodeBlocks(targetTypeMetadata);
6866
this.variableNameFactory = LocalVariableNameFactory.forMethod(targetMethodMetadata);
67+
this.codeBlocks = new CodeBlocks(targetTypeMetadata);
6968
}
7069

7170
AotRepositoryFragmentMetadata getTargetTypeMetadata() {
@@ -129,7 +128,7 @@ public TypeName getReturnTypeName() {
129128
}
130129

131130
/**
132-
* Suggests naming clash free variant for the given intended variable name within the local method context.
131+
* Suggest naming clash free variant for the given intended variable name within the local method context.
133132
*
134133
* @param variableName the intended variable name.
135134
* @return the suggested VariableName
@@ -238,7 +237,7 @@ public List<String> getBindableParameterNames() {
238237
List<String> result = new ArrayList<>();
239238

240239
for (Parameter parameter : queryMethod.getParameters().getBindableParameters()) {
241-
parameter.getName().map(result::add);
240+
getParameterName(parameter.getIndex());
242241
}
243242

244243
return result;
@@ -248,14 +247,7 @@ public List<String> getBindableParameterNames() {
248247
* @return list of all parameter names (including non-bindable special parameters).
249248
*/
250249
public List<String> getAllParameterNames() {
251-
252-
List<String> result = new ArrayList<>();
253-
254-
for (Parameter parameter : queryMethod.getParameters()) {
255-
parameter.getName().map(result::add);
256-
}
257-
258-
return result;
250+
return targetMethodMetadata.getMethodArguments().keySet().stream().toList();
259251
}
260252

261253
public boolean hasField(String fieldName) {
@@ -280,16 +272,7 @@ public String getParameterNameOf(Class<?> type) {
280272
}
281273

282274
public @Nullable String getParameterName(int position) {
283-
284-
if (0 > position) {
285-
return null;
286-
}
287-
288-
List<Entry<String, ParameterSpec>> entries = new ArrayList<>(targetMethodMetadata.getMethodArguments().entrySet());
289-
if (position < entries.size()) {
290-
return entries.get(position).getKey();
291-
}
292-
return null;
275+
return targetMethodMetadata.getParameterName(position);
293276
}
294277

295278
public void addParameter(ParameterSpec parameter) {

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

-24
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,15 @@
1616
package org.springframework.data.repository.aot.generate;
1717

1818
import java.lang.reflect.Method;
19-
import java.lang.reflect.Parameter;
2019
import java.lang.reflect.TypeVariable;
2120
import java.util.function.BiConsumer;
2221
import java.util.function.Function;
2322
import java.util.stream.Collectors;
2423

2524
import javax.lang.model.element.Modifier;
2625

27-
import org.springframework.core.DefaultParameterNameDiscoverer;
28-
import org.springframework.core.MethodParameter;
29-
import org.springframework.core.ResolvableType;
30-
import org.springframework.data.repository.core.RepositoryInformation;
3126
import org.springframework.javapoet.CodeBlock;
3227
import org.springframework.javapoet.MethodSpec;
33-
import org.springframework.javapoet.ParameterSpec;
3428
import org.springframework.javapoet.TypeName;
3529
import org.springframework.javapoet.TypeVariableName;
3630
import org.springframework.util.StringUtils;
@@ -50,25 +44,7 @@ class AotRepositoryMethodBuilder {
5044
private BiConsumer<AotQueryMethodGenerationContext, MethodSpec.Builder> customizer = (context, body) -> {};
5145

5246
AotRepositoryMethodBuilder(AotQueryMethodGenerationContext context) {
53-
5447
this.context = context;
55-
initParameters(context.getMethod(), context.getRepositoryInformation());
56-
}
57-
58-
private void initParameters(Method method, RepositoryInformation repositoryInformation) {
59-
60-
ResolvableType repositoryInterface = ResolvableType.forClass(repositoryInformation.getRepositoryInterface());
61-
62-
for (Parameter parameter : method.getParameters()) {
63-
64-
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
65-
methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
66-
ResolvableType resolvableParameterType = ResolvableType.forMethodParameter(methodParameter, repositoryInterface);
67-
68-
TypeName parameterType = TypeName.get(resolvableParameterType.getType());
69-
70-
this.context.addParameter(ParameterSpec.builder(parameterType, methodParameter.getParameterName()).build());
71-
}
7248
}
7349

7450
/**

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

+42-6
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616
package org.springframework.data.repository.aot.generate;
1717

1818
import java.lang.reflect.Method;
19+
import java.util.ArrayList;
1920
import java.util.LinkedHashMap;
21+
import java.util.List;
2022
import java.util.Map;
2123
import java.util.Map.Entry;
2224

2325
import org.jspecify.annotations.Nullable;
24-
26+
import org.springframework.core.DefaultParameterNameDiscoverer;
27+
import org.springframework.core.MethodParameter;
28+
import org.springframework.core.ParameterNameDiscoverer;
2529
import org.springframework.core.ResolvableType;
2630
import org.springframework.data.repository.core.RepositoryInformation;
2731
import org.springframework.javapoet.ParameterSpec;
@@ -31,17 +35,19 @@
3135
* Metadata about an AOT Repository method.
3236
*
3337
* @author Christoph Strobl
38+
* @since 4.0
3439
*/
3540
class MethodMetadata {
3641

3742
private final Map<String, ParameterSpec> methodArguments = new LinkedHashMap<>();
3843
private final ResolvableType actualReturnType;
3944
private final ResolvableType returnType;
4045

41-
public MethodMetadata(RepositoryInformation repositoryInformation, Method method) {
46+
MethodMetadata(RepositoryInformation repositoryInformation, Method method) {
4247

4348
this.returnType = repositoryInformation.getReturnType(method).toResolvableType();
4449
this.actualReturnType = ResolvableType.forType(repositoryInformation.getReturnedDomainClass(method));
50+
this.initParameters(repositoryInformation, method, new DefaultParameterNameDiscoverer());
4551
}
4652

4753
@Nullable
@@ -54,20 +60,50 @@ public String getParameterNameOf(Class<?> type) {
5460
return null;
5561
}
5662

57-
public ResolvableType getReturnType() {
63+
ResolvableType getReturnType() {
5864
return returnType;
5965
}
6066

61-
public ResolvableType getActualReturnType() {
67+
ResolvableType getActualReturnType() {
6268
return actualReturnType;
6369
}
6470

65-
public void addParameter(ParameterSpec parameterSpec) {
71+
void addParameter(ParameterSpec parameterSpec) {
6672
this.methodArguments.put(parameterSpec.name, parameterSpec);
6773
}
6874

69-
public Map<String, ParameterSpec> getMethodArguments() {
75+
Map<String, ParameterSpec> getMethodArguments() {
7076
return methodArguments;
7177
}
7278

79+
@Nullable
80+
String getParameterName(int position) {
81+
82+
if (0 > position) {
83+
return null;
84+
}
85+
86+
List<Entry<String, ParameterSpec>> entries = new ArrayList<>(methodArguments.entrySet());
87+
if (position < entries.size()) {
88+
return entries.get(position).getKey();
89+
}
90+
return null;
91+
}
92+
93+
private void initParameters(RepositoryInformation repositoryInformation, Method method,
94+
ParameterNameDiscoverer nameDiscoverer) {
95+
96+
ResolvableType repositoryInterface = ResolvableType.forClass(repositoryInformation.getRepositoryInterface());
97+
98+
for (java.lang.reflect.Parameter parameter : method.getParameters()) {
99+
100+
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
101+
methodParameter.initParameterNameDiscovery(nameDiscoverer);
102+
ResolvableType resolvableParameterType = ResolvableType.forMethodParameter(methodParameter, repositoryInterface);
103+
104+
TypeName parameterType = TypeName.get(resolvableParameterType.getType());
105+
106+
addParameter(ParameterSpec.builder(parameterType, methodParameter.getParameterName()).build());
107+
}
108+
}
73109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
* https://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+
package org.springframework.data.repository.aot.generate;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.ArgumentMatchers.eq;
20+
21+
import java.lang.reflect.Method;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mockito;
25+
import org.springframework.data.domain.Pageable;
26+
import org.springframework.data.repository.core.RepositoryInformation;
27+
import org.springframework.data.repository.query.QueryMethod;
28+
import org.springframework.data.util.TypeInformation;
29+
30+
/**
31+
* Tests targeting {@link AotQueryMethodGenerationContext}.
32+
*
33+
* @author Christoph Strobl
34+
*/
35+
class AotQueryMethodGenerationContextUnitTests {
36+
37+
@Test // GH-3270
38+
void suggestLocalVariableNameConsidersMethodArguments() throws NoSuchMethodException {
39+
40+
AotQueryMethodGenerationContext ctx = ctxFor("reservedParameterMethod");
41+
42+
assertThat(ctx.suggestLocalVariableName("foo")).isEqualTo("foo");
43+
assertThat(ctx.suggestLocalVariableName("arg0")).isNotIn("arg0", "arg1", "arg2");
44+
}
45+
46+
AotQueryMethodGenerationContext ctxFor(String methodName) throws NoSuchMethodException {
47+
48+
Method target = null;
49+
for (Method m : DummyRepo.class.getMethods()) {
50+
if (m.getName().equals(methodName)) {
51+
target = m;
52+
break;
53+
}
54+
}
55+
56+
if (target == null) {
57+
throw new NoSuchMethodException(methodName);
58+
}
59+
60+
RepositoryInformation ri = Mockito.mock(RepositoryInformation.class);
61+
Mockito.doReturn(TypeInformation.of(target.getReturnType())).when(ri).getReturnType(eq(target));
62+
63+
return new AotQueryMethodGenerationContext(ri, target, Mockito.mock(QueryMethod.class),
64+
Mockito.mock(AotRepositoryFragmentMetadata.class));
65+
}
66+
67+
private interface DummyRepo {
68+
String reservedParameterMethod(Object arg0, Pageable arg1, Object arg2);
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
* https://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+
package org.springframework.data.repository.aot.generate;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.ArgumentMatchers.eq;
20+
21+
import java.lang.reflect.Method;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mockito;
25+
import org.springframework.data.domain.Pageable;
26+
import org.springframework.data.repository.core.RepositoryInformation;
27+
import org.springframework.data.util.TypeInformation;
28+
29+
/**
30+
* Unit tests for {@link MethodMetadata}.
31+
*
32+
* @author Christoph Strobl
33+
*/
34+
class MethodMetadataUnitTests {
35+
36+
@Test // GH-3270
37+
void getParameterNameByIndex() throws NoSuchMethodException {
38+
39+
MethodMetadata metadata = methodMetadataFor("threeArgsMethod");
40+
41+
assertThat(metadata.getParameterName(0)).isEqualTo("arg0");
42+
assertThat(metadata.getParameterName(1)).isEqualTo("arg1");
43+
assertThat(metadata.getParameterName(2)).isEqualTo("arg2");
44+
}
45+
46+
@Test // GH-3270
47+
void getParameterNameByNonExistingIndex() throws NoSuchMethodException {
48+
49+
MethodMetadata metadata = methodMetadataFor("threeArgsMethod");
50+
51+
assertThat(metadata.getParameterName(-1)).isNull();
52+
assertThat(metadata.getParameterName(3)).isNull();
53+
}
54+
55+
@Test // GH-3270
56+
void getParameterNameForNoArgsMethod() throws NoSuchMethodException {
57+
assertThat(methodMetadataFor("noArgsMethod").getParameterName(0)).isNull();
58+
}
59+
60+
static MethodMetadata methodMetadataFor(String methodName) throws NoSuchMethodException {
61+
62+
Method target = null;
63+
for (Method m : DummyRepo.class.getMethods()) {
64+
if (m.getName().equals(methodName)) {
65+
target = m;
66+
break;
67+
}
68+
}
69+
70+
if (target == null) {
71+
throw new NoSuchMethodException(methodName);
72+
}
73+
74+
RepositoryInformation ri = Mockito.mock(RepositoryInformation.class);
75+
Mockito.doReturn(TypeInformation.of(target.getReturnType())).when(ri).getReturnType(eq(target));
76+
return new MethodMetadata(ri, target);
77+
}
78+
79+
private interface DummyRepo {
80+
81+
String noArgsMethod();
82+
83+
String threeArgsMethod(Object arg0, Pageable arg1, Object arg2);
84+
}
85+
}

0 commit comments

Comments
 (0)