Skip to content

Commit 121f9e3

Browse files
committed
BridgeMethodResolver properly resolves all declared interfaces
Issue: SPR-16288
1 parent ea73ec5 commit 121f9e3

File tree

2 files changed

+97
-14
lines changed

2 files changed

+97
-14
lines changed

spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -196,7 +196,10 @@ private static Method searchInterfaces(Class<?>[] interfaces, Method bridgeMetho
196196
return method;
197197
}
198198
else {
199-
return searchInterfaces(ifc.getInterfaces(), bridgeMethod);
199+
method = searchInterfaces(ifc.getInterfaces(), bridgeMethod);
200+
if (method != null) {
201+
return method;
202+
}
200203
}
201204
}
202205
return null;

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

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,8 +31,10 @@
3131
import java.nio.charset.StandardCharsets;
3232
import java.security.Principal;
3333
import java.text.SimpleDateFormat;
34+
import java.time.Instant;
3435
import java.util.ArrayList;
3536
import java.util.Arrays;
37+
import java.util.Collection;
3638
import java.util.Collections;
3739
import java.util.Date;
3840
import java.util.GregorianCalendar;
@@ -44,6 +46,7 @@
4446
import java.util.Map;
4547
import java.util.Optional;
4648
import java.util.Set;
49+
import java.util.UUID;
4750
import javax.servlet.ServletConfig;
4851
import javax.servlet.ServletContext;
4952
import javax.servlet.http.Cookie;
@@ -1224,13 +1227,22 @@ public void bridgeMethods() throws Exception {
12241227
getServlet().service(request, response);
12251228
}
12261229

1230+
@Test
1231+
public void bridgeMethodsWithMultipleInterfaces() throws Exception {
1232+
initServletWithControllers(ArticleController.class);
1233+
1234+
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/method");
1235+
MockHttpServletResponse response = new MockHttpServletResponse();
1236+
getServlet().service(request, response);
1237+
}
1238+
12271239
@Test
12281240
public void requestParamMap() throws Exception {
12291241
initServletWithControllers(RequestParamMapController.class);
12301242

12311243
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map");
12321244
request.addParameter("key1", "value1");
1233-
request.addParameter("key2", new String[]{"value21", "value22"});
1245+
request.addParameter("key2", new String[] {"value21", "value22"});
12341246
MockHttpServletResponse response = new MockHttpServletResponse();
12351247

12361248
getServlet().service(request, response);
@@ -1249,7 +1261,7 @@ public void requestHeaderMap() throws Exception {
12491261

12501262
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map");
12511263
request.addHeader("Content-Type", "text/html");
1252-
request.addHeader("Custom-Header", new String[]{"value21", "value22"});
1264+
request.addHeader("Custom-Header", new String[] {"value21", "value22"});
12531265
MockHttpServletResponse response = new MockHttpServletResponse();
12541266

12551267
getServlet().service(request, response);
@@ -2591,7 +2603,6 @@ public View resolveViewName(final String viewName, Locale locale) throws Excepti
25912603
public String getContentType() {
25922604
return null;
25932605
}
2594-
25952606
@Override
25962607
@SuppressWarnings({"unchecked", "deprecation", "rawtypes"})
25972608
public void render(@Nullable Map model, HttpServletRequest request, HttpServletResponse response)
@@ -3106,6 +3117,78 @@ public ModelAndView method(MyEntity object) {
31063117
}
31073118
}
31083119

3120+
@RestController
3121+
@RequestMapping(path = ApiConstants.ARTICLES_PATH)
3122+
public static class ArticleController implements ApiConstants, ResourceEndpoint<Article, ArticlePredicate> {
3123+
3124+
@GetMapping(params = "page")
3125+
public Collection<Article> find(String pageable, ArticlePredicate predicate) {
3126+
throw new UnsupportedOperationException("not implemented");
3127+
}
3128+
3129+
@GetMapping
3130+
public List<Article> find(boolean sort, ArticlePredicate predicate) {
3131+
throw new UnsupportedOperationException("not implemented");
3132+
}
3133+
}
3134+
3135+
interface ApiConstants {
3136+
3137+
String API_V1 = "/v1";
3138+
3139+
String ARTICLES_PATH = API_V1 + "/articles";
3140+
}
3141+
3142+
public interface ResourceEndpoint<E extends Entity, P extends EntityPredicate> {
3143+
3144+
Collection<E> find(String pageable, P predicate) throws IOException;
3145+
3146+
List<E> find(boolean sort, P predicate) throws IOException;
3147+
}
3148+
3149+
public static abstract class Entity {
3150+
3151+
public UUID id;
3152+
3153+
public String createdBy;
3154+
3155+
public Instant createdDate;
3156+
}
3157+
3158+
public static class Article extends Entity {
3159+
3160+
public String slug;
3161+
3162+
public String title;
3163+
3164+
public String content;
3165+
}
3166+
3167+
public static abstract class EntityPredicate<E extends Entity> {
3168+
3169+
public String createdBy;
3170+
3171+
public Instant createdBefore;
3172+
3173+
public Instant createdAfter;
3174+
3175+
public boolean accept(E entity) {
3176+
return (createdBy == null || createdBy.equals(entity.createdBy)) &&
3177+
(createdBefore == null || createdBefore.compareTo(entity.createdDate) >= 0) &&
3178+
(createdAfter == null || createdAfter.compareTo(entity.createdDate) >= 0);
3179+
}
3180+
}
3181+
3182+
public static class ArticlePredicate extends EntityPredicate<Article> {
3183+
3184+
public String query;
3185+
3186+
@Override
3187+
public boolean accept(Article entity) {
3188+
return super.accept(entity) && (query == null || (entity.title.contains(query) || entity.content.contains(query)));
3189+
}
3190+
}
3191+
31093192
@Controller
31103193
public static class RequestParamMapController {
31113194

@@ -3277,7 +3360,6 @@ static class TestEntity {
32773360

32783361
private String name;
32793362

3280-
32813363
public String getName() {
32823364
return name;
32833365
}
@@ -3325,16 +3407,14 @@ public void initBinder(WebDataBinder binder) {
33253407
}
33263408

33273409
@RequestMapping("/singleString")
3328-
public void processMultipart(@RequestParam("content") String content,
3329-
HttpServletResponse response) throws IOException {
3330-
3410+
public void processMultipart(@RequestParam("content") String content, HttpServletResponse response)
3411+
throws IOException {
33313412
response.getWriter().write(content);
33323413
}
33333414

33343415
@RequestMapping("/stringArray")
3335-
public void processMultipart(@RequestParam("content") String[] content,
3336-
HttpServletResponse response) throws IOException {
3337-
3416+
public void processMultipart(@RequestParam("content") String[] content, HttpServletResponse response)
3417+
throws IOException {
33383418
response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-"));
33393419
}
33403420
}
@@ -3458,7 +3538,7 @@ public HttpHeaders create() throws URISyntaxException {
34583538

34593539
@RequestMapping(value = "empty", method = RequestMethod.POST)
34603540
@ResponseStatus(HttpStatus.CREATED)
3461-
public HttpHeaders createNoHeader() throws URISyntaxException {
3541+
public HttpHeaders createNoHeader() {
34623542
return new HttpHeaders();
34633543
}
34643544
}

0 commit comments

Comments
 (0)