From 121f9e3734d8998e62c65cb7f230cd88680888b6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 11 Jan 2018 10:18:27 +0100 Subject: [PATCH] BridgeMethodResolver properly resolves all declared interfaces Issue: SPR-16288 --- .../core/BridgeMethodResolver.java | 7 +- ...nnotationControllerHandlerMethodTests.java | 104 ++++++++++++++++-- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index a5d6ec0458..2c7318b8d6 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -196,7 +196,10 @@ public abstract class BridgeMethodResolver { return method; } else { - return searchInterfaces(ifc.getInterfaces(), bridgeMethod); + method = searchInterfaces(ifc.getInterfaces(), bridgeMethod); + if (method != null) { + return method; + } } } return null; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index bd26cd515f..9aec6c60ad 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,10 @@ import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; @@ -44,6 +46,7 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.http.Cookie; @@ -1224,13 +1227,22 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl getServlet().service(request, response); } + @Test + public void bridgeMethodsWithMultipleInterfaces() throws Exception { + initServletWithControllers(ArticleController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/method"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(request, response); + } + @Test public void requestParamMap() throws Exception { initServletWithControllers(RequestParamMapController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map"); request.addParameter("key1", "value1"); - request.addParameter("key2", new String[]{"value21", "value22"}); + request.addParameter("key2", new String[] {"value21", "value22"}); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); @@ -1249,7 +1261,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map"); request.addHeader("Content-Type", "text/html"); - request.addHeader("Custom-Header", new String[]{"value21", "value22"}); + request.addHeader("Custom-Header", new String[] {"value21", "value22"}); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); @@ -2591,7 +2603,6 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl public String getContentType() { return null; } - @Override @SuppressWarnings({"unchecked", "deprecation", "rawtypes"}) public void render(@Nullable Map model, HttpServletRequest request, HttpServletResponse response) @@ -3106,6 +3117,78 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } + @RestController + @RequestMapping(path = ApiConstants.ARTICLES_PATH) + public static class ArticleController implements ApiConstants, ResourceEndpoint { + + @GetMapping(params = "page") + public Collection
find(String pageable, ArticlePredicate predicate) { + throw new UnsupportedOperationException("not implemented"); + } + + @GetMapping + public List
find(boolean sort, ArticlePredicate predicate) { + throw new UnsupportedOperationException("not implemented"); + } + } + + interface ApiConstants { + + String API_V1 = "/v1"; + + String ARTICLES_PATH = API_V1 + "/articles"; + } + + public interface ResourceEndpoint { + + Collection find(String pageable, P predicate) throws IOException; + + List find(boolean sort, P predicate) throws IOException; + } + + public static abstract class Entity { + + public UUID id; + + public String createdBy; + + public Instant createdDate; + } + + public static class Article extends Entity { + + public String slug; + + public String title; + + public String content; + } + + public static abstract class EntityPredicate { + + public String createdBy; + + public Instant createdBefore; + + public Instant createdAfter; + + public boolean accept(E entity) { + return (createdBy == null || createdBy.equals(entity.createdBy)) && + (createdBefore == null || createdBefore.compareTo(entity.createdDate) >= 0) && + (createdAfter == null || createdAfter.compareTo(entity.createdDate) >= 0); + } + } + + public static class ArticlePredicate extends EntityPredicate
{ + + public String query; + + @Override + public boolean accept(Article entity) { + return super.accept(entity) && (query == null || (entity.title.contains(query) || entity.content.contains(query))); + } + } + @Controller public static class RequestParamMapController { @@ -3277,7 +3360,6 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl private String name; - public String getName() { return name; } @@ -3325,16 +3407,14 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } @RequestMapping("/singleString") - public void processMultipart(@RequestParam("content") String content, - HttpServletResponse response) throws IOException { - + public void processMultipart(@RequestParam("content") String content, HttpServletResponse response) + throws IOException { response.getWriter().write(content); } @RequestMapping("/stringArray") - public void processMultipart(@RequestParam("content") String[] content, - HttpServletResponse response) throws IOException { - + public void processMultipart(@RequestParam("content") String[] content, HttpServletResponse response) + throws IOException { response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-")); } } @@ -3458,7 +3538,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @RequestMapping(value = "empty", method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) - public HttpHeaders createNoHeader() throws URISyntaxException { + public HttpHeaders createNoHeader() { return new HttpHeaders(); } }