From 5619b7d2c3f9f055dae6e6aff041b835a9037822 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 28 Aug 2024 22:14:46 +0200 Subject: [PATCH] ProxyHandlerMethodArgumentResolver now avoids matching parameters annotated with Spring annotation. We now explicitly do not match handler method parameters that are annotated with anything but @ModelAttribute or @ProjectedPayload. This prevents us accidentally opting into parameter handling for annotated parameters that use interfaces for their declaration and are supposed to be handled by some other infrastructure. Fixes GH-2937. --- ...ProxyingHandlerMethodArgumentResolver.java | 14 +++++++- .../java/{ => example}/SampleInterface.java | 3 ++ ...andlerMethodArgumentResolverUnitTests.java | 32 ++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) rename src/test/java/{ => example}/SampleInterface.java (97%) diff --git a/src/main/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolver.java b/src/main/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolver.java index a21117aac8..0d4313b4a8 100644 --- a/src/main/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolver.java +++ b/src/main/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolver.java @@ -15,6 +15,7 @@ */ package org.springframework.data.web; +import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.List; @@ -30,6 +31,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.util.ClassUtils; import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; @@ -87,7 +89,8 @@ public boolean supportsParameter(MethodParameter parameter) { } // Annotated parameter - if (parameter.getParameterAnnotation(ProjectedPayload.class) != null) { + if (parameter.getParameterAnnotation(ProjectedPayload.class) != null + || parameter.getParameterAnnotation(ModelAttribute.class) != null) { return true; } @@ -96,6 +99,15 @@ public boolean supportsParameter(MethodParameter parameter) { return true; } + // Exclude parameters annotated with Spring annotation + if (Arrays.stream(parameter.getParameterAnnotations()) + .map(Annotation::annotationType) + .map(Class::getPackageName) + .anyMatch(it -> it.startsWith("org.springframework"))) { + + return false; + } + // Fallback for only user defined interfaces String packageName = ClassUtils.getPackageName(type); diff --git a/src/test/java/SampleInterface.java b/src/test/java/example/SampleInterface.java similarity index 97% rename from src/test/java/SampleInterface.java rename to src/test/java/example/SampleInterface.java index 63cd44993c..df4670612e 100644 --- a/src/test/java/SampleInterface.java +++ b/src/test/java/example/SampleInterface.java @@ -18,4 +18,7 @@ * @author Oliver Gierke * @see org.springframework.data.web.ProxyingHandlerMethodArgumentResolverUnitTests */ +package example; + + public interface SampleInterface {} diff --git a/src/test/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolverUnitTests.java b/src/test/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolverUnitTests.java index ef24f46e24..602efd22fa 100755 --- a/src/test/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolverUnitTests.java +++ b/src/test/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolverUnitTests.java @@ -17,12 +17,16 @@ import static org.assertj.core.api.Assertions.*; +import example.SampleInterface; + import java.util.List; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.web.ProjectingJackson2HttpMessageConverterUnitTests.SampleInterface; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.bind.annotation.ModelAttribute; /** * Unit tests for {@link ProxyingHandlerMethodArgumentResolver}. @@ -72,6 +76,28 @@ void doesNotSupportCoreJavaType() throws Exception { assertThat(resolver.supportsParameter(parameter)).isFalse(); } + @Test // GH-2937 + void doesNotSupportForeignSpringAnnotations() throws Exception { + + var parameter = getParameter("withForeignAnnotation", SampleInterface.class); + + assertThat(resolver.supportsParameter(parameter)).isFalse(); + } + + @Test // GH-2937 + void doesSupportAtModelAttribute() throws Exception { + + var parameter = getParameter("withModelAttribute", SampleInterface.class); + + assertThat(resolver.supportsParameter(parameter)).isTrue(); + } + + private static MethodParameter getParameter(String methodName, Class parameterType) { + + var method = ReflectionUtils.findMethod(Controller.class, methodName, parameterType); + return new MethodParameter(method, 0); + } + @ProjectedPayload interface AnnotatedInterface {} @@ -86,5 +112,9 @@ interface Controller { void with(SampleInterface param); void with(List param); + + void withForeignAnnotation(@Autowired SampleInterface param); + + void withModelAttribute(@ModelAttribute SampleInterface param); } }