Skip to content

Commit 63a89ed

Browse files
committed
Fix @Argument annotation detection in generic interfaces
When a GraphQL handler method is defined in a generic interface, parameter annotations such as @argument may not be correctly detected on the concrete implementation. This occurs because the bridged method matching in HandlerMethod uses ResolvableType.resolve() which can fail to match the erased parameter types of the bridged method. This commit updates the parameter type comparison to use ResolvableType.toClass() instead, ensuring that the raw classes are correctly matched and annotations on generic interface methods are properly discovered. Fixes gh-1469
1 parent b53f2e9 commit 63a89ed

2 files changed

Lines changed: 98 additions & 1 deletion

File tree

spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ private boolean isOverrideFor(Method candidate) {
281281
}
282282
for (int i = 0; i < paramTypes.length; i++) {
283283
if (paramTypes[i] !=
284-
ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).resolve()) {
284+
ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).toClass()) {
285285
return false;
286286
}
287287
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2002-present 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+
17+
package org.springframework.graphql.data.method;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.Method;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.core.MethodParameter;
25+
import org.springframework.graphql.data.method.annotation.Argument;
26+
import org.springframework.util.ClassUtils;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Tests for {@link HandlerMethod}.
32+
*
33+
* @author Brian Clozel
34+
*/
35+
class HandlerMethodTests {
36+
37+
38+
@Test
39+
void shouldFindArgumentAnnotationInGenericInterface() {
40+
Object target = new GenericInterfaceImpl();
41+
42+
HandlerMethod handlerMethod1 = getHandlerMethod(target, "fetchOneArg", Long.class);
43+
MethodParameter[] methodParameters1 = handlerMethod1.getMethodParameters();
44+
assertThat(methodParameters1).hasSize(1);
45+
Annotation[] parameterAnnotations1 = methodParameters1[0].getParameterAnnotations();
46+
assertThat(parameterAnnotations1).hasSize(1);
47+
assertThat(parameterAnnotations1[0].annotationType()).isEqualTo(Argument.class);
48+
49+
HandlerMethod handlerMethod2 = getHandlerMethod(target, "fetchTwoArg", Long.class, Object.class);
50+
MethodParameter[] methodParameters2 = handlerMethod2.getMethodParameters();
51+
assertThat(methodParameters2).hasSize(2);
52+
Annotation[] parameterAnnotations2_1 = methodParameters2[0].getParameterAnnotations();
53+
assertThat(parameterAnnotations2_1).hasSize(1);
54+
assertThat(parameterAnnotations2_1[0].annotationType()).isEqualTo(Argument.class);
55+
Annotation[] parameterAnnotations2_2 = methodParameters2[1].getParameterAnnotations();
56+
assertThat(parameterAnnotations2_2).hasSize(1);
57+
assertThat(parameterAnnotations2_2[0].annotationType()).isEqualTo(Argument.class);
58+
}
59+
60+
61+
private static HandlerMethod getHandlerMethod(Object target, String methodName, Class<?>... parameterTypes) {
62+
Method method = ClassUtils.getMethod(target.getClass(), methodName, parameterTypes);
63+
return new HandlerMethod(target, method);
64+
}
65+
66+
67+
interface GenericInterface<A, B> {
68+
69+
void fetchOneArg(@Argument A arg1);
70+
71+
void fetchTwoArg(@Argument A arg1, @Argument B arg2);
72+
}
73+
74+
abstract static class GenericAbstractSuperclass<C> implements GenericInterface<Long, C> {
75+
76+
@Override
77+
public void fetchOneArg(Long arg1) {
78+
79+
}
80+
81+
@Override
82+
public void fetchTwoArg(Long arg1, C arg2) {
83+
84+
}
85+
86+
public abstract void fetchTwoArg(@Argument C arg);
87+
}
88+
89+
static class GenericInterfaceImpl extends GenericAbstractSuperclass<String> {
90+
91+
@Override
92+
public void fetchTwoArg(String arg) {
93+
94+
}
95+
}
96+
97+
}

0 commit comments

Comments
 (0)