Consistently expose parameter annotations from base classes as well
Closes gh-25788
This commit is contained in:
parent
37fa82c578
commit
bb1cdb6b48
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -28,7 +28,6 @@ import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -54,7 +53,7 @@ public class AnnotatedMethod {
|
||||||
private final MethodParameter[] parameters;
|
private final MethodParameter[] parameters;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile List<Annotation[][]> interfaceParameterAnnotations;
|
private volatile List<Annotation[][]> inheritedParameterAnnotations;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +76,7 @@ public class AnnotatedMethod {
|
||||||
this.method = annotatedMethod.method;
|
this.method = annotatedMethod.method;
|
||||||
this.bridgedMethod = annotatedMethod.bridgedMethod;
|
this.bridgedMethod = annotatedMethod.bridgedMethod;
|
||||||
this.parameters = annotatedMethod.parameters;
|
this.parameters = annotatedMethod.parameters;
|
||||||
this.interfaceParameterAnnotations = annotatedMethod.interfaceParameterAnnotations;
|
this.inheritedParameterAnnotations = annotatedMethod.inheritedParameterAnnotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,18 +163,32 @@ public class AnnotatedMethod {
|
||||||
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
|
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Annotation[][]> getInterfaceParameterAnnotations() {
|
private List<Annotation[][]> getInheritedParameterAnnotations() {
|
||||||
List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations;
|
List<Annotation[][]> parameterAnnotations = this.inheritedParameterAnnotations;
|
||||||
if (parameterAnnotations == null) {
|
if (parameterAnnotations == null) {
|
||||||
parameterAnnotations = new ArrayList<>();
|
parameterAnnotations = new ArrayList<>();
|
||||||
for (Class<?> ifc : ClassUtils.getAllInterfacesForClassAsSet(this.method.getDeclaringClass())) {
|
Class<?> clazz = this.method.getDeclaringClass();
|
||||||
for (Method candidate : ifc.getMethods()) {
|
while (clazz != null) {
|
||||||
if (isOverrideFor(candidate)) {
|
for (Class<?> ifc : clazz.getInterfaces()) {
|
||||||
parameterAnnotations.add(candidate.getParameterAnnotations());
|
for (Method candidate : ifc.getMethods()) {
|
||||||
|
if (isOverrideFor(candidate)) {
|
||||||
|
parameterAnnotations.add(candidate.getParameterAnnotations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
if (clazz == Object.class) {
|
||||||
|
clazz = null;
|
||||||
|
}
|
||||||
|
if (clazz != null) {
|
||||||
|
for (Method candidate : clazz.getMethods()) {
|
||||||
|
if (isOverrideFor(candidate)) {
|
||||||
|
parameterAnnotations.add(candidate.getParameterAnnotations());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.interfaceParameterAnnotations = parameterAnnotations;
|
this.inheritedParameterAnnotations = parameterAnnotations;
|
||||||
}
|
}
|
||||||
return parameterAnnotations;
|
return parameterAnnotations;
|
||||||
}
|
}
|
||||||
|
@ -281,7 +294,7 @@ public class AnnotatedMethod {
|
||||||
anns = super.getParameterAnnotations();
|
anns = super.getParameterAnnotations();
|
||||||
int index = getParameterIndex();
|
int index = getParameterIndex();
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) {
|
for (Annotation[][] ifcAnns : getInheritedParameterAnnotations()) {
|
||||||
if (index < ifcAnns.length) {
|
if (index < ifcAnns.length) {
|
||||||
Annotation[] paramAnns = ifcAnns[index];
|
Annotation[] paramAnns = ifcAnns[index];
|
||||||
if (paramAnns.length > 0) {
|
if (paramAnns.length > 0) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -765,6 +765,24 @@ class RequestResponseBodyMethodProcessorTests {
|
||||||
assertThat(value).isEqualTo("foo");
|
assertThat(value).isEqualTo("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-25788
|
||||||
|
void resolveArgumentTypeVariableWithAbstractMethod() throws Exception {
|
||||||
|
this.servletRequest.setContent("\"foo\"".getBytes(StandardCharsets.UTF_8));
|
||||||
|
this.servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
|
||||||
|
Method method = SubControllerImplementingAbstractMethod.class.getMethod("handle", Object.class);
|
||||||
|
HandlerMethod handlerMethod = new HandlerMethod(new SubControllerImplementingAbstractMethod(), method);
|
||||||
|
MethodParameter methodParameter = handlerMethod.getMethodParameters()[0];
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> converters = List.of(new MappingJackson2HttpMessageConverter());
|
||||||
|
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
|
||||||
|
|
||||||
|
assertThat(processor.supportsParameter(methodParameter)).isTrue();
|
||||||
|
String value = (String) processor.readWithMessageConverters(
|
||||||
|
this.request, methodParameter, methodParameter.getGenericParameterType());
|
||||||
|
assertThat(value).isEqualTo("foo");
|
||||||
|
}
|
||||||
|
|
||||||
private void assertContentDisposition(RequestResponseBodyMethodProcessor processor,
|
private void assertContentDisposition(RequestResponseBodyMethodProcessor processor,
|
||||||
boolean expectContentDisposition, String requestURI, String comment) throws Exception {
|
boolean expectContentDisposition, String requestURI, String comment) throws Exception {
|
||||||
|
|
||||||
|
@ -1149,4 +1167,19 @@ class RequestResponseBodyMethodProcessorTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract static class MyControllerWithAbstractMethod<A> {
|
||||||
|
|
||||||
|
public abstract A handle(@RequestBody A arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class SubControllerImplementingAbstractMethod extends MyControllerWithAbstractMethod<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String handle(String arg) {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue