Skip to content

Commit

Permalink
ProxyHandlerMethodArgumentResolver now avoids matching parameters ann…
Browse files Browse the repository at this point in the history
…otated 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.
  • Loading branch information
odrotbohm committed Aug 28, 2024
1 parent d50d9b6 commit 5619b7d
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.web;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@
* @author Oliver Gierke
* @see org.springframework.data.web.ProxyingHandlerMethodArgumentResolverUnitTests
*/
package example;


public interface SampleInterface {}
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand Down Expand Up @@ -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 {}

Expand All @@ -86,5 +112,9 @@ interface Controller {
void with(SampleInterface param);

void with(List<Object> param);

void withForeignAnnotation(@Autowired SampleInterface param);

void withModelAttribute(@ModelAttribute SampleInterface param);
}
}

0 comments on commit 5619b7d

Please sign in to comment.