SPR-8487 Ensure setters for argument resolvers and return value handlers replace the defaults completely.

This commit is contained in:
Rossen Stoyanchev 2011-06-27 22:23:10 +00:00
parent 8504830da1
commit 78470782d4
5 changed files with 158 additions and 24 deletions

View File

@ -122,9 +122,9 @@ import org.springframework.web.util.WebUtils;
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
InitializingBean { InitializingBean {
private List<HandlerMethodArgumentResolver> customArgumentResolvers; private List<? extends HandlerMethodArgumentResolver> customArgumentResolvers;
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers; private List<? extends HandlerMethodReturnValueHandler> customReturnValueHandlers;
private List<ModelAndViewResolver> modelAndViewResolvers; private List<ModelAndViewResolver> modelAndViewResolvers;
@ -179,7 +179,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* <p>An existing {@link WebArgumentResolver} can either adapted with {@link ServletWebArgumentResolverAdapter} * <p>An existing {@link WebArgumentResolver} can either adapted with {@link ServletWebArgumentResolverAdapter}
* or preferably converted to a {@link HandlerMethodArgumentResolver} instead. * or preferably converted to a {@link HandlerMethodArgumentResolver} instead.
*/ */
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { public void setCustomArgumentResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
this.customArgumentResolvers = argumentResolvers; this.customArgumentResolvers = argumentResolvers;
} }
@ -189,7 +189,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* {@link #setCustomArgumentResolvers(List)}, which does not override default registrations. * {@link #setCustomArgumentResolvers(List)}, which does not override default registrations.
* @param argumentResolvers argument resolvers for {@link RequestMapping} and {@link ModelAttribute} methods * @param argumentResolvers argument resolvers for {@link RequestMapping} and {@link ModelAttribute} methods
*/ */
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { public void setArgumentResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers != null) { if (argumentResolvers != null) {
this.argumentResolvers = new HandlerMethodArgumentResolverComposite(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
this.argumentResolvers.addResolvers(argumentResolvers); this.argumentResolvers.addResolvers(argumentResolvers);
@ -202,7 +202,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* {@link #setCustomArgumentResolvers(List)}, which does not override default registrations. * {@link #setCustomArgumentResolvers(List)}, which does not override default registrations.
* @param argumentResolvers argument resolvers for {@link InitBinder} methods * @param argumentResolvers argument resolvers for {@link InitBinder} methods
*/ */
public void setInitBinderArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { public void setInitBinderArgumentResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers != null) { if (argumentResolvers != null) {
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite();
this.initBinderArgumentResolvers.addResolvers(argumentResolvers); 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)}. * and others. Those handlers can only be customized via {@link #setReturnValueHandlers(List)}.
* @param returnValueHandlers custom return value handlers for {@link RequestMapping} methods * @param returnValueHandlers custom return value handlers for {@link RequestMapping} methods
*/ */
public void setCustomReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { public void setCustomReturnValueHandlers(List<? extends HandlerMethodReturnValueHandler> returnValueHandlers) {
this.customReturnValueHandlers = returnValueHandlers; this.customReturnValueHandlers = returnValueHandlers;
} }
@ -226,7 +226,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
* {@link #setCustomReturnValueHandlers(List)}, which does not override default registrations. * {@link #setCustomReturnValueHandlers(List)}, which does not override default registrations.
* @param returnValueHandlers the return value handlers for {@link RequestMapping} methods * @param returnValueHandlers the return value handlers for {@link RequestMapping} methods
*/ */
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { public void setReturnValueHandlers(List<? extends HandlerMethodReturnValueHandler> returnValueHandlers) {
if (returnValueHandlers != null) { if (returnValueHandlers != null) {
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
this.returnValueHandlers.addHandlers(returnValueHandlers); this.returnValueHandlers.addHandlers(returnValueHandlers);
@ -340,10 +340,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
} }
private void initArgumentResolvers() { private void initArgumentResolvers() {
if (argumentResolvers == null) { if (argumentResolvers != null) {
argumentResolvers = new HandlerMethodArgumentResolverComposite(); return;
} }
argumentResolvers = new HandlerMethodArgumentResolverComposite();
// Annotation-based resolvers // Annotation-based resolvers
argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false)); argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
argumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver()); argumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver());
@ -371,10 +373,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
} }
private void initInitBinderArgumentResolvers() { private void initInitBinderArgumentResolvers() {
if (initBinderArgumentResolvers == null) { if (initBinderArgumentResolvers != null) {
initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); return;
} }
initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite();
// Annotation-based resolvers // Annotation-based resolvers
initBinderArgumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false)); initBinderArgumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
initBinderArgumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver()); initBinderArgumentResolvers.addResolver(new RequestParamMapMethodArgumentResolver());
@ -382,7 +386,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
initBinderArgumentResolvers.addResolver(new ExpressionValueMethodArgumentResolver(beanFactory)); initBinderArgumentResolvers.addResolver(new ExpressionValueMethodArgumentResolver(beanFactory));
// Custom resolvers // Custom resolvers
argumentResolvers.addResolvers(customArgumentResolvers); initBinderArgumentResolvers.addResolvers(customArgumentResolvers);
// Type-based resolvers // Type-based resolvers
initBinderArgumentResolvers.addResolver(new ServletRequestMethodArgumentResolver()); initBinderArgumentResolvers.addResolver(new ServletRequestMethodArgumentResolver());
@ -393,10 +397,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
} }
private void initReturnValueHandlers() { private void initReturnValueHandlers() {
if (returnValueHandlers == null) { if (returnValueHandlers != null) {
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); return;
} }
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
// Annotation-based handlers // Annotation-based handlers
returnValueHandlers.addHandler(new RequestResponseBodyMethodProcessor(messageConverters)); returnValueHandlers.addHandler(new RequestResponseBodyMethodProcessor(messageConverters));
returnValueHandlers.addHandler(new ModelAttributeMethodProcessor(false)); returnValueHandlers.addHandler(new ModelAttributeMethodProcessor(false));

View File

@ -111,10 +111,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* @param handlerType the handler type * @param handlerType the handler type
* @return a {@link RequestMappingInfo} instance; never {@code null} * @return a {@link RequestMappingInfo} instance; never {@code null}
*/ */
RequestMappingInfo createRequestMappingInfo(RequestMapping annot, protected RequestMappingInfo createRequestMappingInfo(RequestMapping annot,
boolean isMethodAnnotation, boolean isMethodAnnotation,
Method method, Method method,
Class<?> handlerType) { Class<?> handlerType) {
return new RequestMappingInfo( return new RequestMappingInfo(
new PatternsRequestCondition(annot.value(), getUrlPathHelper(), getPathMatcher(), useSuffixPatternMatch), new PatternsRequestCondition(annot.value(), getUrlPathHelper(), getPathMatcher(), useSuffixPatternMatch),
new RequestMethodsRequestCondition(annot.method()), new RequestMethodsRequestCondition(annot.method()),

View File

@ -20,15 +20,29 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; 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.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.SessionAttributes; 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.context.support.GenericWebApplicationContext;
import org.springframework.web.method.HandlerMethod; 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.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
/** /**
* Fine-grained {@link RequestMappingHandlerAdapter} unit tests. * Fine-grained {@link RequestMappingHandlerAdapter} unit tests.
@ -36,8 +50,8 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
* <p>For higher-level adapter tests see: * <p>For higher-level adapter tests see:
* <ul> * <ul>
* <li>{@link ServletHandlerMethodTests} * <li>{@link ServletHandlerMethodTests}
* <li>{@link HandlerMethodAnnotationDetectionTests}
* <li>{@link RequestMappingHandlerAdapterIntegrationTests} * <li>{@link RequestMappingHandlerAdapterIntegrationTests}
* <li>{@link HandlerMethodAdapterAnnotationDetectionTests}
* </ul> * </ul>
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -54,7 +68,6 @@ public class RequestMappingHandlerAdapterTests {
public void setup() throws Exception { public void setup() throws Exception {
this.handlerAdapter = new RequestMappingHandlerAdapter(); this.handlerAdapter = new RequestMappingHandlerAdapter();
this.handlerAdapter.setApplicationContext(new GenericWebApplicationContext()); this.handlerAdapter.setApplicationContext(new GenericWebApplicationContext());
this.handlerAdapter.afterPropertiesSet();
this.request = new MockHttpServletRequest(); this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse(); this.response = new MockHttpServletResponse();
@ -62,6 +75,7 @@ public class RequestMappingHandlerAdapterTests {
@Test @Test
public void cacheControlWithoutSessionAttributes() throws Exception { public void cacheControlWithoutSessionAttributes() throws Exception {
handlerAdapter.afterPropertiesSet();
handlerAdapter.setCacheSeconds(100); handlerAdapter.setCacheSeconds(100);
handlerAdapter.handle(request, response, handlerMethod(new SimpleHandler(), "handle")); handlerAdapter.handle(request, response, handlerMethod(new SimpleHandler(), "handle"));
@ -70,17 +84,131 @@ public class RequestMappingHandlerAdapterTests {
@Test @Test
public void cacheControlWithSessionAttributes() throws Exception { public void cacheControlWithSessionAttributes() throws Exception {
handlerAdapter.afterPropertiesSet();
handlerAdapter.setCacheSeconds(100); handlerAdapter.setCacheSeconds(100);
handlerAdapter.handle(request, response, handlerMethod(new SessionAttributeHandler(), "handle")); handlerAdapter.handle(request, response, handlerMethod(new SessionAttributeHandler(), "handle"));
assertEquals("no-cache", response.getHeader("Cache-Control")); assertEquals("no-cache", response.getHeader("Cache-Control"));
} }
@Test
@SuppressWarnings("unchecked")
public void setArgumentResolvers() {
List<HandlerMethodArgumentResolver> expected = new ArrayList<HandlerMethodArgumentResolver>();
expected.add(new ServletRequestMethodArgumentResolver());
handlerAdapter.setArgumentResolvers(expected);
handlerAdapter.afterPropertiesSet();
HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite)
new DirectFieldAccessor(handlerAdapter).getPropertyValue("argumentResolvers");
List<HandlerMethodArgumentResolver> actual = (List<HandlerMethodArgumentResolver>)
new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers");
assertEquals(expected, actual);
}
@Test
@SuppressWarnings("unchecked")
public void setInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> expected = new ArrayList<HandlerMethodArgumentResolver>();
expected.add(new ServletRequestMethodArgumentResolver());
handlerAdapter.setInitBinderArgumentResolvers(expected);
handlerAdapter.afterPropertiesSet();
HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite)
new DirectFieldAccessor(handlerAdapter).getPropertyValue("initBinderArgumentResolvers");
List<HandlerMethodArgumentResolver> actual = (List<HandlerMethodArgumentResolver>)
new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers");
assertEquals(expected, actual);
}
@Test
@SuppressWarnings("unchecked")
public void setReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> expected = new ArrayList<HandlerMethodReturnValueHandler>();
expected.add(new ModelMethodProcessor());
handlerAdapter.setReturnValueHandlers(expected);
handlerAdapter.afterPropertiesSet();
HandlerMethodReturnValueHandlerComposite composite = (HandlerMethodReturnValueHandlerComposite)
new DirectFieldAccessor(handlerAdapter).getPropertyValue("returnValueHandlers");
List<HandlerMethodReturnValueHandler> actual = (List<HandlerMethodReturnValueHandler>)
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<HandlerMethodArgumentResolver> actual = (List<HandlerMethodArgumentResolver>)
new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers");
assertTrue(actual.contains(resolver));
composite = (HandlerMethodArgumentResolverComposite)
new DirectFieldAccessor(handlerAdapter).getPropertyValue("initBinderArgumentResolvers");
actual = (List<HandlerMethodArgumentResolver>)
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<HandlerMethodReturnValueHandler> actual = (List<HandlerMethodReturnValueHandler>)
new DirectFieldAccessor(composite).getPropertyValue("returnValueHandlers");
assertTrue(actual.contains(handler));
}
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception { private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes); Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
return new InvocableHandlerMethod(handler, method); 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 { private static class SimpleHandler {
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -102,7 +102,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
/** /**
* Add the given {@link HandlerMethodArgumentResolver}s. * Add the given {@link HandlerMethodArgumentResolver}s.
*/ */
public void addResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { public void addResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers != null) { if (argumentResolvers != null) {
for (HandlerMethodArgumentResolver resolver : argumentResolvers) { for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
this.argumentResolvers.add(resolver); this.argumentResolvers.add(resolver);

View File

@ -101,7 +101,7 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
/** /**
* Add the given {@link HandlerMethodReturnValueHandler}s. * Add the given {@link HandlerMethodReturnValueHandler}s.
*/ */
public void addHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { public void addHandlers(List<? extends HandlerMethodReturnValueHandler> returnValueHandlers) {
if (returnValueHandlers != null) { if (returnValueHandlers != null) {
for (HandlerMethodReturnValueHandler handler : returnValueHandlers) { for (HandlerMethodReturnValueHandler handler : returnValueHandlers) {
this.returnValueHandlers.add(handler); this.returnValueHandlers.add(handler);