Skip to content

Commit 4d34444

Browse files
committed
Consistent annotation lookup in MvcUriComponentsBuilder
Previously, a UriComponents build based on a method would require the given method to be the annotated one as method parameter resolution only applied locally. This was a problem when a controller was specified on a method whose mapping is defined in a parent. This commit harmonies the lookup using AnnotatedMethod who provides support for a synthesized method parameter that takes annotations from parent parameter into account. Closes gh-32553
1 parent d4ddbd5 commit 4d34444

File tree

2 files changed

+38
-11
lines changed

2 files changed

+38
-11
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java

+13-11
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import org.springframework.core.MethodParameter;
4343
import org.springframework.core.ParameterNameDiscoverer;
4444
import org.springframework.core.annotation.AnnotatedElementUtils;
45-
import org.springframework.core.annotation.SynthesizingMethodParameter;
45+
import org.springframework.core.annotation.AnnotatedMethod;
4646
import org.springframework.lang.Nullable;
4747
import org.springframework.objenesis.ObjenesisException;
4848
import org.springframework.objenesis.SpringObjenesis;
@@ -538,22 +538,23 @@ public UriComponentsBuilder withMethod(Class<?> controllerType, Method method, O
538538
private static UriComponentsBuilder fromMethodInternal(@Nullable UriComponentsBuilder builder,
539539
Class<?> controllerType, Method method, Object... args) {
540540

541+
AnnotatedMethod annotatedMethod = new AnnotatedMethod(method);
541542
builder = getBaseUrlToUse(builder);
542543

543544
// Externally configured prefix via PathConfigurer..
544545
String prefix = getPathPrefix(controllerType);
545546
builder.path(prefix);
546547

547548
String typePath = getClassMapping(controllerType);
548-
String methodPath = getMethodMapping(method);
549+
String methodPath = getMethodMapping(annotatedMethod);
549550
String path = pathMatcher.combine(typePath, methodPath);
550551
path = PathPatternParser.defaultInstance.initFullPathPattern(path);
551552
if (!StringUtils.hasText(prefix + path)) {
552553
path = "/";
553554
}
554555
builder.path(path);
555556

556-
return applyContributors(builder, method, args);
557+
return applyContributors(builder, annotatedMethod, args);
557558
}
558559

559560
private static UriComponentsBuilder getBaseUrlToUse(@Nullable UriComponentsBuilder baseUrl) {
@@ -587,13 +588,12 @@ private static String getClassMapping(Class<?> controllerType) {
587588
return getPathMapping(mapping, controllerType.getName());
588589
}
589590

590-
private static String getMethodMapping(Method method) {
591-
Assert.notNull(method, "'method' must not be null");
592-
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
591+
private static String getMethodMapping(AnnotatedMethod annotatedMethod) {
592+
RequestMapping requestMapping = annotatedMethod.getMethodAnnotation(RequestMapping.class);
593593
if (requestMapping == null) {
594-
throw new IllegalArgumentException("No @RequestMapping on: " + method.toGenericString());
594+
throw new IllegalArgumentException("No @RequestMapping on: " + annotatedMethod.getMethod().toGenericString());
595595
}
596-
return getPathMapping(requestMapping, method.toGenericString());
596+
return getPathMapping(requestMapping, annotatedMethod.getMethod().toGenericString());
597597
}
598598

599599
private static String getPathMapping(RequestMapping requestMapping, String source) {
@@ -628,10 +628,12 @@ else if (methods.size() > 1) {
628628
}
629629
}
630630

631-
private static UriComponentsBuilder applyContributors(UriComponentsBuilder builder, Method method, Object... args) {
631+
private static UriComponentsBuilder applyContributors(UriComponentsBuilder builder,
632+
AnnotatedMethod annotatedMethod, Object... args) {
633+
632634
CompositeUriComponentsContributor contributor = getUriComponentsContributor();
633635

634-
int paramCount = method.getParameterCount();
636+
int paramCount = annotatedMethod.getMethodParameters().length;
635637
int argCount = args.length;
636638
if (paramCount != argCount) {
637639
throw new IllegalArgumentException("Number of method parameters " + paramCount +
@@ -640,7 +642,7 @@ private static UriComponentsBuilder applyContributors(UriComponentsBuilder build
640642

641643
final Map<String, Object> uriVars = new HashMap<>();
642644
for (int i = 0; i < paramCount; i++) {
643-
MethodParameter param = new SynthesizingMethodParameter(method, i);
645+
MethodParameter param = annotatedMethod.getMethodParameters()[i];
644646
param.initParameterNameDiscovery(parameterNameDiscoverer);
645647
contributor.contributeMethodArgument(param, args[i], builder, uriVars);
646648
}

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

+25
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.format.annotation.DateTimeFormat.ISO;
4343
import org.springframework.http.HttpEntity;
4444
import org.springframework.http.MediaType;
45+
import org.springframework.http.ResponseEntity;
4546
import org.springframework.lang.Nullable;
4647
import org.springframework.stereotype.Controller;
4748
import org.springframework.util.MultiValueMap;
@@ -334,6 +335,14 @@ void fromMethodNameConfigurablePath() {
334335
assertThat(uriComponents.toUriString()).isEqualTo("http://localhost/something/custom/1/foo");
335336
}
336337

338+
@Test
339+
void fromMethodNameWithAnnotationsOnInterface() {
340+
initWebApplicationContext(WebConfig.class);
341+
UriComponents uriComponents = fromMethodName(HelloController.class, "get", "test").build();
342+
343+
assertThat(uriComponents.toString()).isEqualTo("http://localhost/hello/test");
344+
}
345+
337346
@Test
338347
void fromMethodCallOnSubclass() {
339348
UriComponents uriComponents = fromMethodCall(on(ExtendedController.class).myMethod(null)).build();
@@ -855,4 +864,20 @@ public Savepoint getBooking(@PathVariable Long booking) {
855864
}
856865
}
857866

867+
interface HelloInterface {
868+
869+
@GetMapping("/hello/{name}")
870+
ResponseEntity<String> get(@PathVariable String name);
871+
}
872+
873+
@Controller
874+
static class HelloController implements HelloInterface {
875+
876+
@Override
877+
public ResponseEntity<String> get(String name) {
878+
return ResponseEntity.ok("Hello " + name);
879+
}
880+
881+
}
882+
858883
}

0 commit comments

Comments
 (0)