diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index db1c04f3d0c..da565f3b23f 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -487,8 +487,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator boolean match = false; if (mappingInfo.paths.length > 0) { List matchedPaths = new ArrayList(mappingInfo.paths.length); - for (String methodLevelPattern : mappingInfo.paths) { - String matchedPattern = getMatchedPattern(methodLevelPattern, lookupPath, request); + for (String mappedPattern : mappingInfo.paths) { + if (!hasTypeLevelMapping() && !mappedPattern.startsWith("/")) { + mappedPattern = "/" + mappedPattern; + } + String matchedPattern = getMatchedPattern(mappedPattern, lookupPath, request); if (matchedPattern != null) { if (mappingInfo.matches(request)) { match = true; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java index df51b64f252..7e883855428 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java @@ -154,11 +154,11 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler /** * Derive URL mappings from the handler's method-level mappings. * @param handlerType the handler type to introspect - * @param indicateEmpty whether the returned array should contain - * null in case of an empty {@link RequestMapping} value. + * @param hasTypeLevelMapping whether the method-level mappings are nested + * within a type-level mapping * @return the array of mapped URLs */ - protected String[] determineUrlsForHandlerMethods(Class handlerType, final boolean indicateEmpty) { + protected String[] determineUrlsForHandlerMethods(Class handlerType, final boolean hasTypeLevelMapping) { String[] subclassResult = determineUrlsForHandlerMethods(handlerType); if (subclassResult != null) { return subclassResult; @@ -175,10 +175,13 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler String[] mappedPatterns = mapping.value(); if (mappedPatterns.length > 0) { for (String mappedPattern : mappedPatterns) { + if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) { + mappedPattern = "/" + mappedPattern; + } addUrlsForPath(urls, mappedPattern); } } - else if (indicateEmpty) { + else if (hasTypeLevelMapping) { // empty method-level RequestMapping urls.add(null); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index 96378b52b4c..ea00315a008 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -964,6 +964,32 @@ public class ServletAnnotationControllerTests { assertEquals("mySurpriseView", response.getContentAsString()); } + @Test + public void relativeMethodPathDispatchingController() throws Exception { + initServlet(MyRelativeMethodPathDispatchingController.class); + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myView", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/yourApp/myOther"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myOtherView", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/hisApp/myLang"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myLangView", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/herApp/surprise.do"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("mySurpriseView", response.getContentAsString()); + } + @Test public void nullCommandController() throws Exception { initServlet(MyNullCommandController.class); @@ -1360,8 +1386,8 @@ public class ServletAnnotationControllerTests { } /* - * Controllers - */ + * Controllers + */ @RequestMapping("/myPath.do") private static class MyController extends AbstractController { @@ -1836,6 +1862,30 @@ public class ServletAnnotationControllerTests { } } + @Controller + private static class MyRelativeMethodPathDispatchingController { + + @RequestMapping("**/myHandle") + public void myHandle(HttpServletResponse response) throws IOException { + response.getWriter().write("myView"); + } + + @RequestMapping("/**/*Other") + public void myOtherHandle(HttpServletResponse response) throws IOException { + response.getWriter().write("myOtherView"); + } + + @RequestMapping("**/myLang") + public void myLangHandle(HttpServletResponse response) throws IOException { + response.getWriter().write("myLangView"); + } + + @RequestMapping("/**/surprise") + public void mySurpriseHandle(HttpServletResponse response) throws IOException { + response.getWriter().write("mySurpriseView"); + } + } + @Controller private static class MyNullCommandController {