From 78470782d49055ffc60fcd21e2d2529d45673112 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 27 Jun 2011 22:23:10 +0000 Subject: [PATCH] SPR-8487 Ensure setters for argument resolvers and return value handlers replace the defaults completely. --- .../RequestMappingHandlerAdapter.java | 38 ++--- .../RequestMappingHandlerMapping.java | 8 +- .../RequestMappingHandlerAdapterTests.java | 132 +++++++++++++++++- ...andlerMethodArgumentResolverComposite.java | 2 +- ...dlerMethodReturnValueHandlerComposite.java | 2 +- 5 files changed, 158 insertions(+), 24 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 72b100d861..b662cc8f6e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -122,9 +122,9 @@ import org.springframework.web.util.WebUtils; public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { - private List customArgumentResolvers; + private List customArgumentResolvers; - private List customReturnValueHandlers; + private List customReturnValueHandlers; private List modelAndViewResolvers; @@ -179,7 +179,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i *

An existing {@link WebArgumentResolver} can either adapted with {@link ServletWebArgumentResolverAdapter} * or preferably converted to a {@link HandlerMethodArgumentResolver} instead. */ - public void setCustomArgumentResolvers(List argumentResolvers) { + public void setCustomArgumentResolvers(List argumentResolvers) { this.customArgumentResolvers = argumentResolvers; } @@ -189,7 +189,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i * {@link #setCustomArgumentResolvers(List)}, which does not override default registrations. * @param argumentResolvers argument resolvers for {@link RequestMapping} and {@link ModelAttribute} methods */ - public void setArgumentResolvers(List argumentResolvers) { + public void setArgumentResolvers(List argumentResolvers) { if (argumentResolvers != null) { this.argumentResolvers = new HandlerMethodArgumentResolverComposite(); this.argumentResolvers.addResolvers(argumentResolvers); @@ -202,7 +202,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i * {@link #setCustomArgumentResolvers(List)}, which does not override default registrations. * @param argumentResolvers argument resolvers for {@link InitBinder} methods */ - public void setInitBinderArgumentResolvers(List argumentResolvers) { + public void setInitBinderArgumentResolvers(List argumentResolvers) { if (argumentResolvers != null) { this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); this.initBinderArgumentResolvers.addResolvers(argumentResolvers); @@ -216,7 +216,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i * and others. Those handlers can only be customized via {@link #setReturnValueHandlers(List)}. * @param returnValueHandlers custom return value handlers for {@link RequestMapping} methods */ - public void setCustomReturnValueHandlers(List returnValueHandlers) { + public void setCustomReturnValueHandlers(List returnValueHandlers) { this.customReturnValueHandlers = returnValueHandlers; } @@ -226,7 +226,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i * {@link #setCustomReturnValueHandlers(List)}, which does not override default registrations. * @param returnValueHandlers the return value handlers for {@link RequestMapping} methods */ - public void setReturnValueHandlers(List returnValueHandlers) { + public void setReturnValueHandlers(List returnValueHandlers) { if (returnValueHandlers != null) { this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); this.returnValueHandlers.addHandlers(returnValueHandlers); @@ -340,10 +340,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } private void initArgumentResolvers() { - if (argumentResolvers == null) { - argumentResolvers = new HandlerMethodArgumentResolverComposite(); + if (argumentResolvers != null) { + return; } - + + argumentResolvers = new HandlerMethodArgumentResolverComposite(); + // Annotation-based resolvers argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false)); argumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver()); @@ -371,10 +373,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } private void initInitBinderArgumentResolvers() { - if (initBinderArgumentResolvers == null) { - initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); + if (initBinderArgumentResolvers != null) { + return; } + initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); + // Annotation-based resolvers initBinderArgumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false)); initBinderArgumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver()); @@ -382,7 +386,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i initBinderArgumentResolvers.addResolver(new ExpressionValueMethodArgumentResolver(beanFactory)); // Custom resolvers - argumentResolvers.addResolvers(customArgumentResolvers); + initBinderArgumentResolvers.addResolvers(customArgumentResolvers); // Type-based resolvers initBinderArgumentResolvers.addResolver(new ServletRequestMethodArgumentResolver()); @@ -393,10 +397,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } private void initReturnValueHandlers() { - if (returnValueHandlers == null) { - returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); + if (returnValueHandlers != null) { + return; } - + + returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); + // Annotation-based handlers returnValueHandlers.addHandler(new RequestResponseBodyMethodProcessor(messageConverters)); returnValueHandlers.addHandler(new ModelAttributeMethodProcessor(false)); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 51cd709799..a2f0dec7c1 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -111,10 +111,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * @param handlerType the handler type * @return a {@link RequestMappingInfo} instance; never {@code null} */ - RequestMappingInfo createRequestMappingInfo(RequestMapping annot, - boolean isMethodAnnotation, - Method method, - Class handlerType) { + protected RequestMappingInfo createRequestMappingInfo(RequestMapping annot, + boolean isMethodAnnotation, + Method method, + Class handlerType) { return new RequestMappingInfo( new PatternsRequestCondition(annot.value(), getUrlPathHelper(), getPathMatcher(), useSuffixPatternMatch), new RequestMethodsRequestCondition(annot.method()), diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java index 94dfb5914f..ff30727bc2 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java @@ -20,15 +20,29 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.core.MethodParameter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.method.HandlerMethod; +import org.springframework.web.method.annotation.support.ModelMethodProcessor; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; +import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.InvocableHandlerMethod; +import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver; /** * Fine-grained {@link RequestMappingHandlerAdapter} unit tests. @@ -36,8 +50,8 @@ import org.springframework.web.method.support.InvocableHandlerMethod; *

For higher-level adapter tests see: *

    *
  • {@link ServletHandlerMethodTests} + *
  • {@link HandlerMethodAnnotationDetectionTests} *
  • {@link RequestMappingHandlerAdapterIntegrationTests} - *
  • {@link HandlerMethodAdapterAnnotationDetectionTests} *
* * @author Rossen Stoyanchev @@ -54,7 +68,6 @@ public class RequestMappingHandlerAdapterTests { public void setup() throws Exception { this.handlerAdapter = new RequestMappingHandlerAdapter(); this.handlerAdapter.setApplicationContext(new GenericWebApplicationContext()); - this.handlerAdapter.afterPropertiesSet(); this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); @@ -62,6 +75,7 @@ public class RequestMappingHandlerAdapterTests { @Test public void cacheControlWithoutSessionAttributes() throws Exception { + handlerAdapter.afterPropertiesSet(); handlerAdapter.setCacheSeconds(100); handlerAdapter.handle(request, response, handlerMethod(new SimpleHandler(), "handle")); @@ -70,17 +84,131 @@ public class RequestMappingHandlerAdapterTests { @Test public void cacheControlWithSessionAttributes() throws Exception { + handlerAdapter.afterPropertiesSet(); handlerAdapter.setCacheSeconds(100); handlerAdapter.handle(request, response, handlerMethod(new SessionAttributeHandler(), "handle")); assertEquals("no-cache", response.getHeader("Cache-Control")); } + @Test + @SuppressWarnings("unchecked") + public void setArgumentResolvers() { + List expected = new ArrayList(); + expected.add(new ServletRequestMethodArgumentResolver()); + handlerAdapter.setArgumentResolvers(expected); + handlerAdapter.afterPropertiesSet(); + + HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("argumentResolvers"); + + List actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); + + assertEquals(expected, actual); + } + + @Test + @SuppressWarnings("unchecked") + public void setInitBinderArgumentResolvers() { + List expected = new ArrayList(); + expected.add(new ServletRequestMethodArgumentResolver()); + handlerAdapter.setInitBinderArgumentResolvers(expected); + handlerAdapter.afterPropertiesSet(); + + HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("initBinderArgumentResolvers"); + + List actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); + + assertEquals(expected, actual); + } + + @Test + @SuppressWarnings("unchecked") + public void setReturnValueHandlers() { + List expected = new ArrayList(); + expected.add(new ModelMethodProcessor()); + handlerAdapter.setReturnValueHandlers(expected); + handlerAdapter.afterPropertiesSet(); + + HandlerMethodReturnValueHandlerComposite composite = (HandlerMethodReturnValueHandlerComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("returnValueHandlers"); + + List actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("returnValueHandlers"); + + assertEquals(expected, actual); + } + + @Test + @SuppressWarnings("unchecked") + public void setCustomArgumentResolvers() { + TestHanderMethodArgumentResolver resolver = new TestHanderMethodArgumentResolver(); + handlerAdapter.setCustomArgumentResolvers(Arrays.asList(resolver)); + handlerAdapter.afterPropertiesSet(); + + HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("argumentResolvers"); + + List actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); + + assertTrue(actual.contains(resolver)); + + composite = (HandlerMethodArgumentResolverComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("initBinderArgumentResolvers"); + + actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); + + assertTrue(actual.contains(resolver)); + } + + @Test + @SuppressWarnings("unchecked") + public void setCustomReturnValueHandlers() { + TestHandlerMethodReturnValueHandler handler = new TestHandlerMethodReturnValueHandler(); + handlerAdapter.setCustomReturnValueHandlers(Arrays.asList(handler)); + handlerAdapter.afterPropertiesSet(); + + HandlerMethodReturnValueHandlerComposite composite = (HandlerMethodReturnValueHandlerComposite) + new DirectFieldAccessor(handlerAdapter).getPropertyValue("returnValueHandlers"); + + List actual = (List) + new DirectFieldAccessor(composite).getPropertyValue("returnValueHandlers"); + + assertTrue(actual.contains(handler)); + } + private HandlerMethod handlerMethod(Object handler, String methodName, Class... paramTypes) throws Exception { Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes); return new InvocableHandlerMethod(handler, method); } + private final class TestHanderMethodArgumentResolver implements HandlerMethodArgumentResolver { + public boolean supportsParameter(MethodParameter parameter) { + return false; + } + + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + return null; + } + } + + private final class TestHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler{ + + public boolean supportsReturnType(MethodParameter returnType) { + return false; + } + + public void handleReturnValue(Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { + } + } + private static class SimpleHandler { @SuppressWarnings("unused") diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java index 4cd0531996..4e847098b8 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java @@ -102,7 +102,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu /** * Add the given {@link HandlerMethodArgumentResolver}s. */ - public void addResolvers(List argumentResolvers) { + public void addResolvers(List argumentResolvers) { if (argumentResolvers != null) { for (HandlerMethodArgumentResolver resolver : argumentResolvers) { this.argumentResolvers.add(resolver); diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java index bc813a1c82..0208e17d2b 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java @@ -101,7 +101,7 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe /** * Add the given {@link HandlerMethodReturnValueHandler}s. */ - public void addHandlers(List returnValueHandlers) { + public void addHandlers(List returnValueHandlers) { if (returnValueHandlers != null) { for (HandlerMethodReturnValueHandler handler : returnValueHandlers) { this.returnValueHandlers.add(handler);