Browse Source

revised handler method resolution, in particular with respect to generic interfaces (SPR-7355)

pull/1234/head
Juergen Hoeller 15 years ago
parent
commit
35971f9f90
  1. 12
      org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java
  2. 10
      org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java
  3. 116
      org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java
  4. 45
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java
  5. 9
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java
  6. 221
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
  7. 21
      org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java

12
org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java

@ -622,4 +622,16 @@ public abstract class ReflectionUtils { @@ -622,4 +622,16 @@ public abstract class ReflectionUtils {
}
};
/**
* Pre-built MethodFilter that matches all non-bridge methods
* which are not declared on <code>java.lang.Object</code>.
*/
public static MethodFilter USER_DECLARED_METHODS = new MethodFilter() {
public boolean matches(Method method) {
return (!method.isBridge() && method.getDeclaringClass() != Object.class);
}
};
}

10
org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java

@ -435,6 +435,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator @@ -435,6 +435,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator
@Override
protected boolean isHandlerMethod(Method method) {
if (this.mappings.containsKey(method)) {
return true;
}
RequestMappingInfo mappingInfo = new RequestMappingInfo();
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
ActionMapping actionMapping = AnnotationUtils.findAnnotation(method, ActionMapping.class);
@ -460,8 +463,11 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator @@ -460,8 +463,11 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator
mappingInfo.phase = determineDefaultPhase(method);
}
}
this.mappings.put(method, mappingInfo);
return (mappingInfo.phase != null);
if (mappingInfo.phase != null) {
this.mappings.put(method, mappingInfo);
return true;
}
return false;
}
public Method resolveHandlerMethod(PortletRequest request) throws PortletException {

116
org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/DefaultAnnotationHandlerMapping.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@ -21,6 +21,7 @@ import java.lang.reflect.Method; @@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.portlet.ClientDataRequest;
@ -145,71 +146,76 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp @@ -145,71 +146,76 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
* @return <code>true</code> if at least 1 handler method has been registered;
* <code>false</code> otherwise
*/
protected boolean detectHandlerMethods(Class handlerType, final String beanName, final RequestMapping typeMapping) {
protected boolean detectHandlerMethods(Class<?> handlerType, final String beanName, final RequestMapping typeMapping) {
final Set<Boolean> handlersRegistered = new HashSet<Boolean>(1);
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
boolean mappingFound = false;
String[] modeKeys = new String[0];
String[] params = new String[0];
String resourceId = null;
String eventName = null;
for (Annotation ann : method.getAnnotations()) {
if (AnnotationUtils.findAnnotation(ann.getClass(), Mapping.class) != null) {
mappingFound = true;
if (ann instanceof RequestMapping) {
RequestMapping rm = (RequestMapping) ann;
modeKeys = rm.value();
params = StringUtils.mergeStringArrays(params, rm.params());
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
handlerTypes.add(handlerType);
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
boolean mappingFound = false;
String[] modeKeys = new String[0];
String[] params = new String[0];
String resourceId = null;
String eventName = null;
for (Annotation ann : method.getAnnotations()) {
if (AnnotationUtils.findAnnotation(ann.getClass(), Mapping.class) != null) {
mappingFound = true;
if (ann instanceof RequestMapping) {
RequestMapping rm = (RequestMapping) ann;
modeKeys = rm.value();
params = StringUtils.mergeStringArrays(params, rm.params());
}
else if (ann instanceof ResourceMapping) {
ResourceMapping rm = (ResourceMapping) ann;
resourceId = rm.value();
}
else if (ann instanceof EventMapping) {
EventMapping em = (EventMapping) ann;
eventName = em.value();
}
else {
String[] specificParams = (String[]) AnnotationUtils.getValue(ann, "params");
params = StringUtils.mergeStringArrays(params, specificParams);
}
}
else if (ann instanceof ResourceMapping) {
ResourceMapping rm = (ResourceMapping) ann;
resourceId = rm.value();
}
if (mappingFound) {
if (modeKeys.length == 0) {
if (typeMapping != null) {
modeKeys = typeMapping.value();
}
else {
throw new IllegalStateException(
"No portlet mode mappings specified - neither at type nor at method level");
}
}
else if (ann instanceof EventMapping) {
EventMapping em = (EventMapping) ann;
eventName = em.value();
if (typeMapping != null) {
if (!PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value())) {
throw new IllegalStateException("Mode mappings conflict between method and type level: " +
Arrays.asList(modeKeys) + " versus " + Arrays.asList(typeMapping.value()));
}
params = StringUtils.mergeStringArrays(typeMapping.params(), params);
}
else {
String[] specificParams = (String[]) AnnotationUtils.getValue(ann, "params");
params = StringUtils.mergeStringArrays(params, specificParams);
PortletRequestMappingPredicate predicate;
if (resourceId != null) {
predicate = new ResourceMappingPredicate(resourceId);
}
}
}
if (mappingFound) {
if (modeKeys.length == 0) {
if (typeMapping != null) {
modeKeys = typeMapping.value();
else if (eventName != null) {
predicate = new EventMappingPredicate(eventName);
}
else {
throw new IllegalStateException(
"No portlet mode mappings specified - neither at type nor at method level");
predicate = new ParameterMappingPredicate(params);
}
}
if (typeMapping != null) {
if (!PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value())) {
throw new IllegalStateException("Mode mappings conflict between method and type level: " +
Arrays.asList(modeKeys) + " versus " + Arrays.asList(typeMapping.value()));
for (String modeKey : modeKeys) {
registerHandler(new PortletMode(modeKey), beanName, predicate);
handlersRegistered.add(Boolean.TRUE);
}
params = StringUtils.mergeStringArrays(typeMapping.params(), params);
}
PortletRequestMappingPredicate predicate;
if (resourceId != null) {
predicate = new ResourceMappingPredicate(resourceId);
}
else if (eventName != null) {
predicate = new EventMappingPredicate(eventName);
}
else {
predicate = new ParameterMappingPredicate(params);
}
for (String modeKey : modeKeys) {
registerHandler(new PortletMode(modeKey), beanName, predicate);
handlersRegistered.add(Boolean.TRUE);
}
}
}
});
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return !handlersRegistered.isEmpty();
}

45
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java

@ -27,6 +27,7 @@ import java.util.ArrayList; @@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@ -496,10 +497,36 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator @@ -496,10 +497,36 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
*/
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>();
private ServletHandlerMethodResolver(Class<?> handlerType) {
init(handlerType);
}
@Override
protected boolean isHandlerMethod(Method method) {
if (this.mappings.containsKey(method)) {
return true;
}
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (mapping != null) {
RequestMappingInfo mappingInfo = new RequestMappingInfo();
mappingInfo.patterns = mapping.value();
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
mappingInfo.methods = mapping.method();
}
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
mappingInfo.params = mapping.params();
}
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
mappingInfo.headers = mapping.headers();
}
this.mappings.put(method, mappingInfo);
return true;
}
return false;
}
public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
@ -507,7 +534,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator @@ -507,7 +534,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
Set<String> allowedMethods = new LinkedHashSet<String>(7);
String resolvedMethodName = null;
for (Method handlerMethod : getHandlerMethods()) {
RequestMappingInfo mappingInfo = createRequestMappingInfo(handlerMethod);
RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod);
boolean match = false;
if (mappingInfo.hasPatterns()) {
List<String> matchingPatterns = new ArrayList<String>(mappingInfo.patterns.length);
@ -599,22 +626,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator @@ -599,22 +626,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
}
}
private RequestMappingInfo createRequestMappingInfo(Method handlerMethod) {
RequestMappingInfo mappingInfo = new RequestMappingInfo();
RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
mappingInfo.patterns = mapping.value();
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
mappingInfo.methods = mapping.method();
}
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
mappingInfo.params = mapping.params();
}
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
mappingInfo.headers = mapping.headers();
}
return mappingInfo;
}
/**
* Determines the combined pattern for the given methodLevelPattern and path.
* <p>Uses the following algorithm: <ol>

9
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
package org.springframework.web.servlet.mvc.annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@ -165,8 +165,9 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler @@ -165,8 +165,9 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
}
final Set<String> urls = new LinkedHashSet<String>();
Class<?>[] handlerTypes =
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[]{handlerType};
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
handlerTypes.add(handlerType);
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
@ -187,7 +188,7 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler @@ -187,7 +188,7 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
}
}
}
}, ReflectionUtils.NON_BRIDGED_METHODS);
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return StringUtils.toStringArray(urls);
}

221
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java

@ -174,6 +174,27 @@ public class ServletAnnotationControllerTests { @@ -174,6 +174,27 @@ public class ServletAnnotationControllerTests {
assertEquals("test", response.getContentAsString());
}
@Test
public void emptyValueMapping() throws Exception {
servlet = new DispatcherServlet() {
@Override
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
GenericWebApplicationContext wac = new GenericWebApplicationContext();
wac.registerBeanDefinition("controller", new RootBeanDefinition(ControllerWithEmptyValueMapping.class));
wac.refresh();
return wac;
}
};
servlet.init(new MockServletConfig());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContextPath("/foo");
request.setServletPath("");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("test", response.getContentAsString());
}
@Test
public void customAnnotationController() throws Exception {
initServlet(CustomAnnotationController.class);
@ -382,6 +403,7 @@ public class ServletAnnotationControllerTests { @@ -382,6 +403,7 @@ public class ServletAnnotationControllerTests {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page1", request.getAttribute("viewName"));
HttpSession session = request.getSession();
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
@ -392,6 +414,7 @@ public class ServletAnnotationControllerTests { @@ -392,6 +414,7 @@ public class ServletAnnotationControllerTests {
request.setSession(session);
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page2", request.getAttribute("viewName"));
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
@ -419,20 +442,97 @@ public class ServletAnnotationControllerTests { @@ -419,20 +442,97 @@ public class ServletAnnotationControllerTests {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page1", request.getAttribute("viewName"));
HttpSession session = request.getSession();
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
request = new MockHttpServletRequest("POST", "/myPage");
request.setSession(session);
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page2", request.getAttribute("viewName"));
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
}
@Test
public void parameterizedAnnotatedInterface() throws Exception {
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
@Override
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
GenericWebApplicationContext wac = new GenericWebApplicationContext();
wac.registerBeanDefinition("controller", new RootBeanDefinition(MyParameterizedControllerImpl.class));
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
wac.refresh();
return wac;
}
};
servlet.init(new MockServletConfig());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page1", request.getAttribute("viewName"));
HttpSession session = request.getSession();
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
request = new MockHttpServletRequest("POST", "/myPage");
request.setSession(session);
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page2", request.getAttribute("viewName"));
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
}
@Test
public void parameterizedAnnotatedInterfaceWithOverriddenMappingsInImpl() throws Exception {
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
@Override
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
GenericWebApplicationContext wac = new GenericWebApplicationContext();
wac.registerBeanDefinition("controller",
new RootBeanDefinition(MyParameterizedControllerImplWithOverriddenMappings.class));
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
wac.refresh();
return wac;
}
};
servlet.init(new MockServletConfig());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page1", request.getAttribute("viewName"));
HttpSession session = request.getSession();
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
request = new MockHttpServletRequest("POST", "/myPage");
request.setSession(session);
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("page2", request.getAttribute("viewName"));
assertTrue(session.getAttribute("object1") != null);
assertTrue(session.getAttribute("object2") != null);
assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
}
@Test
@ -1599,6 +1699,7 @@ public class ServletAnnotationControllerTests { @@ -1599,6 +1699,7 @@ public class ServletAnnotationControllerTests {
assertEquals("test-{foo=bar}", response.getContentAsString());
}
/*
* Controllers
*/
@ -1623,6 +1724,20 @@ public class ServletAnnotationControllerTests { @@ -1623,6 +1724,20 @@ public class ServletAnnotationControllerTests {
}
}
@Controller
private static class ControllerWithEmptyValueMapping {
@RequestMapping("")
public void myPath2(HttpServletResponse response) throws IOException {
response.getWriter().write("test");
}
@RequestMapping("/bar")
public void myPath3(HttpServletResponse response) throws IOException {
response.getWriter().write("testX");
}
}
@Controller
private static class MyAdaptedController {
@ -1632,10 +1747,8 @@ public class ServletAnnotationControllerTests { @@ -1632,10 +1747,8 @@ public class ServletAnnotationControllerTests {
}
@RequestMapping("/myPath2.do")
public void myHandle(@RequestParam("param1") String p1,
@RequestParam("param2") int p2,
@RequestHeader("header1") long h1,
@CookieValue("cookie1") Cookie c1,
public void myHandle(@RequestParam("param1") String p1, @RequestParam("param2") int p2,
@RequestHeader("header1") long h1, @CookieValue("cookie1") Cookie c1,
HttpServletResponse response) throws IOException {
response.getWriter().write("test-" + p1 + "-" + p2 + "-" + h1 + "-" + c1.getValue());
}
@ -1661,11 +1774,8 @@ public class ServletAnnotationControllerTests { @@ -1661,11 +1774,8 @@ public class ServletAnnotationControllerTests {
}
@RequestMapping("/myPath2.do")
public void myHandle(@RequestParam("param1") String p1,
int param2,
HttpServletResponse response,
@RequestHeader("header1") String h1,
@CookieValue("cookie1") String c1) throws IOException {
public void myHandle(@RequestParam("param1") String p1, int param2, HttpServletResponse response,
@RequestHeader("header1") String h1, @CookieValue("cookie1") String c1) throws IOException {
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + h1 + "-" + c1);
}
@ -1684,11 +1794,8 @@ public class ServletAnnotationControllerTests { @@ -1684,11 +1794,8 @@ public class ServletAnnotationControllerTests {
private static class MyAdaptedControllerBase<T> {
@RequestMapping("/myPath2.do")
public void myHandle(@RequestParam("param1") T p1,
int param2,
@RequestHeader Integer header1,
@CookieValue int cookie1,
HttpServletResponse response) throws IOException {
public void myHandle(@RequestParam("param1") T p1, int param2, @RequestHeader Integer header1,
@CookieValue int cookie1, HttpServletResponse response) throws IOException {
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
}
@ -1712,11 +1819,8 @@ public class ServletAnnotationControllerTests { @@ -1712,11 +1819,8 @@ public class ServletAnnotationControllerTests {
}
@Override
public void myHandle(@RequestParam("param1") String p1,
int param2,
@RequestHeader Integer header1,
@CookieValue int cookie1,
HttpServletResponse response) throws IOException {
public void myHandle(@RequestParam("param1") String p1, int param2, @RequestHeader Integer header1,
@CookieValue int cookie1, HttpServletResponse response) throws IOException {
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
}
@ -1768,13 +1872,13 @@ public class ServletAnnotationControllerTests { @@ -1768,13 +1872,13 @@ public class ServletAnnotationControllerTests {
public String get(Model model) {
model.addAttribute("object1", new Object());
model.addAttribute("object2", new Object());
return "myPage";
return "page1";
}
@RequestMapping(method = RequestMethod.POST)
public String post(@ModelAttribute("object1") Object object1) {
//do something with object1
return "myPage";
return "page2";
}
}
@ -1796,14 +1900,79 @@ public class ServletAnnotationControllerTests { @@ -1796,14 +1900,79 @@ public class ServletAnnotationControllerTests {
public String get(Model model) {
model.addAttribute("object1", new Object());
model.addAttribute("object2", new Object());
return "myPage";
return "page1";
}
public String post(@ModelAttribute("object1") Object object1) {
//do something with object1
return "myPage";
return "page2";
}
}
@RequestMapping("/myPage")
@SessionAttributes({"object1", "object2"})
public interface MyParameterizedControllerIfc<T> {
@ModelAttribute("testBeanList")
List<TestBean> getTestBeans();
@RequestMapping(method = RequestMethod.GET)
String get(Model model);
}
public interface MyEditableParameterizedControllerIfc<T> extends MyParameterizedControllerIfc<T> {
@RequestMapping(method = RequestMethod.POST)
String post(@ModelAttribute("object1") T object);
}
@Controller
public static class MyParameterizedControllerImpl implements MyEditableParameterizedControllerIfc<TestBean> {
public List<TestBean> getTestBeans() {
List<TestBean> list = new LinkedList<TestBean>();
list.add(new TestBean("tb1"));
list.add(new TestBean("tb2"));
return list;
}
public String get(Model model) {
model.addAttribute("object1", new TestBean());
model.addAttribute("object2", new TestBean());
return "page1";
}
public String post(TestBean object) {
//do something with object1
return "page2";
}
}
@Controller
public static class MyParameterizedControllerImplWithOverriddenMappings implements MyEditableParameterizedControllerIfc<TestBean> {
@ModelAttribute("testBeanList")
public List<TestBean> getTestBeans() {
List<TestBean> list = new LinkedList<TestBean>();
list.add(new TestBean("tb1"));
list.add(new TestBean("tb2"));
return list;
}
@RequestMapping(method = RequestMethod.GET)
public String get(Model model) {
model.addAttribute("object1", new TestBean());
model.addAttribute("object2", new TestBean());
return "page1";
}
@RequestMapping(method = RequestMethod.POST)
public String post(@ModelAttribute("object1") TestBean object1) {
//do something with object1
return "page2";
}
}
@Controller
public static class MyFormController {
@ -2191,15 +2360,15 @@ public class ServletAnnotationControllerTests { @@ -2191,15 +2360,15 @@ public class ServletAnnotationControllerTests {
}
}
private static class ModelExposingViewResolver implements ViewResolver {
public static class ModelExposingViewResolver implements ViewResolver {
public View resolveViewName(String viewName, Locale locale) throws Exception {
public View resolveViewName(final String viewName, Locale locale) throws Exception {
return new View() {
public String getContentType() {
return null;
}
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
request.setAttribute("viewName", viewName);
request.getSession().setAttribute("model", model);
}
};

21
org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@ -17,13 +17,13 @@ @@ -17,13 +17,13 @@
package org.springframework.web.bind.annotation.support;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@ -70,14 +70,17 @@ public class HandlerMethodResolver { @@ -70,14 +70,17 @@ public class HandlerMethodResolver {
* Initialize a new HandlerMethodResolver for the specified handler type.
* @param handlerType the handler class to introspect
*/
public void init(Class<?> handlerType) {
Class<?>[] handlerTypes =
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[] {handlerType};
for (final Class<?> currentHandlerType : handlerTypes) {
public void init(final Class<?> handlerType) {
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
handlerTypes.add(handlerType);
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, currentHandlerType);
if (isHandlerMethod(method)) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, handlerType);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (isHandlerMethod(specificMethod) &&
(bridgedMethod == specificMethod || !isHandlerMethod(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
else if (method.isAnnotationPresent(InitBinder.class)) {
@ -87,7 +90,7 @@ public class HandlerMethodResolver { @@ -87,7 +90,7 @@ public class HandlerMethodResolver {
modelAttributeMethods.add(specificMethod);
}
}
}, ReflectionUtils.NON_BRIDGED_METHODS);
}, ReflectionUtils.USER_DECLARED_METHODS);
}
this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
SessionAttributes sessionAttributes = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);

Loading…
Cancel
Save