Skip to content

Commit bb1cdb6

Browse files
committed
Consistently expose parameter annotations from base classes as well
Closes gh-25788
1 parent 37fa82c commit bb1cdb6

File tree

2 files changed

+59
-13
lines changed

2 files changed

+59
-13
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java

+25-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@
2828
import org.springframework.lang.NonNull;
2929
import org.springframework.lang.Nullable;
3030
import org.springframework.util.Assert;
31-
import org.springframework.util.ClassUtils;
3231
import org.springframework.util.ObjectUtils;
3332
import org.springframework.util.ReflectionUtils;
3433
import org.springframework.util.StringUtils;
@@ -54,7 +53,7 @@ public class AnnotatedMethod {
5453
private final MethodParameter[] parameters;
5554

5655
@Nullable
57-
private volatile List<Annotation[][]> interfaceParameterAnnotations;
56+
private volatile List<Annotation[][]> inheritedParameterAnnotations;
5857

5958

6059
/**
@@ -77,7 +76,7 @@ protected AnnotatedMethod(AnnotatedMethod annotatedMethod) {
7776
this.method = annotatedMethod.method;
7877
this.bridgedMethod = annotatedMethod.bridgedMethod;
7978
this.parameters = annotatedMethod.parameters;
80-
this.interfaceParameterAnnotations = annotatedMethod.interfaceParameterAnnotations;
79+
this.inheritedParameterAnnotations = annotatedMethod.inheritedParameterAnnotations;
8180
}
8281

8382

@@ -164,18 +163,32 @@ public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationTyp
164163
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
165164
}
166165

167-
private List<Annotation[][]> getInterfaceParameterAnnotations() {
168-
List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations;
166+
private List<Annotation[][]> getInheritedParameterAnnotations() {
167+
List<Annotation[][]> parameterAnnotations = this.inheritedParameterAnnotations;
169168
if (parameterAnnotations == null) {
170169
parameterAnnotations = new ArrayList<>();
171-
for (Class<?> ifc : ClassUtils.getAllInterfacesForClassAsSet(this.method.getDeclaringClass())) {
172-
for (Method candidate : ifc.getMethods()) {
173-
if (isOverrideFor(candidate)) {
174-
parameterAnnotations.add(candidate.getParameterAnnotations());
170+
Class<?> clazz = this.method.getDeclaringClass();
171+
while (clazz != null) {
172+
for (Class<?> ifc : clazz.getInterfaces()) {
173+
for (Method candidate : ifc.getMethods()) {
174+
if (isOverrideFor(candidate)) {
175+
parameterAnnotations.add(candidate.getParameterAnnotations());
176+
}
177+
}
178+
}
179+
clazz = clazz.getSuperclass();
180+
if (clazz == Object.class) {
181+
clazz = null;
182+
}
183+
if (clazz != null) {
184+
for (Method candidate : clazz.getMethods()) {
185+
if (isOverrideFor(candidate)) {
186+
parameterAnnotations.add(candidate.getParameterAnnotations());
187+
}
175188
}
176189
}
177190
}
178-
this.interfaceParameterAnnotations = parameterAnnotations;
191+
this.inheritedParameterAnnotations = parameterAnnotations;
179192
}
180193
return parameterAnnotations;
181194
}
@@ -281,7 +294,7 @@ public Annotation[] getParameterAnnotations() {
281294
anns = super.getParameterAnnotations();
282295
int index = getParameterIndex();
283296
if (index >= 0) {
284-
for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) {
297+
for (Annotation[][] ifcAnns : getInheritedParameterAnnotations()) {
285298
if (index < ifcAnns.length) {
286299
Annotation[] paramAnns = ifcAnns[index];
287300
if (paramAnns.length > 0) {

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java

+34-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -765,6 +765,24 @@ void resolveArgumentTypeVariableWithGenericInterfaceAndSubclass() throws Excepti
765765
assertThat(value).isEqualTo("foo");
766766
}
767767

768+
@Test // gh-25788
769+
void resolveArgumentTypeVariableWithAbstractMethod() throws Exception {
770+
this.servletRequest.setContent("\"foo\"".getBytes(StandardCharsets.UTF_8));
771+
this.servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
772+
773+
Method method = SubControllerImplementingAbstractMethod.class.getMethod("handle", Object.class);
774+
HandlerMethod handlerMethod = new HandlerMethod(new SubControllerImplementingAbstractMethod(), method);
775+
MethodParameter methodParameter = handlerMethod.getMethodParameters()[0];
776+
777+
List<HttpMessageConverter<?>> converters = List.of(new MappingJackson2HttpMessageConverter());
778+
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
779+
780+
assertThat(processor.supportsParameter(methodParameter)).isTrue();
781+
String value = (String) processor.readWithMessageConverters(
782+
this.request, methodParameter, methodParameter.getGenericParameterType());
783+
assertThat(value).isEqualTo("foo");
784+
}
785+
768786
private void assertContentDisposition(RequestResponseBodyMethodProcessor processor,
769787
boolean expectContentDisposition, String requestURI, String comment) throws Exception {
770788

@@ -1149,4 +1167,19 @@ public String handle(String arg) {
11491167
}
11501168
}
11511169

1170+
1171+
abstract static class MyControllerWithAbstractMethod<A> {
1172+
1173+
public abstract A handle(@RequestBody A arg);
1174+
}
1175+
1176+
1177+
static class SubControllerImplementingAbstractMethod extends MyControllerWithAbstractMethod<String> {
1178+
1179+
@Override
1180+
public String handle(String arg) {
1181+
return arg;
1182+
}
1183+
}
1184+
11521185
}

0 commit comments

Comments
 (0)