Skip to content

Commit 313546a

Browse files
committed
SPR-8234 Argument resolver and return value handler configuration improvements
1 parent 68b4687 commit 313546a

15 files changed

+418
-234
lines changed

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/MvcAnnotationDriven.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.springframework.validation.Validator;
3131
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
3232
import org.springframework.web.bind.support.WebArgumentResolver;
33+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
34+
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
3335

3436
/**
3537
* Specifies the Spring MVC "annotation-driven" container feature. The
@@ -167,9 +169,16 @@ boolean shouldRegisterDefaultMessageConverters() {
167169
return this.shouldRegisterDefaultMessageConverters;
168170
}
169171

172+
public MvcAnnotationDriven argumentResolvers(HandlerMethodArgumentResolver... resolvers) {
173+
for (HandlerMethodArgumentResolver resolver : resolvers) {
174+
this.argumentResolvers.add(resolver);
175+
}
176+
return this;
177+
}
178+
170179
public MvcAnnotationDriven argumentResolvers(WebArgumentResolver... resolvers) {
171180
for (WebArgumentResolver resolver : resolvers) {
172-
this.argumentResolvers.add(resolver);
181+
this.argumentResolvers.add(new ServletWebArgumentResolverAdapter(resolver));
173182
}
174183
return this;
175184
}

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodAdapter.java

+192-131
Large diffs are not rendered by default.

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMethodExceptionResolver.java

+109-59
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.ArrayList;
21+
import java.util.List;
2022
import java.util.Map;
2123
import java.util.Set;
2224
import java.util.concurrent.ConcurrentHashMap;
@@ -80,9 +82,13 @@
8082
public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandlerMethodExceptionResolver implements
8183
InitializingBean {
8284

83-
private WebArgumentResolver[] customArgumentResolvers;
85+
private final List<HandlerMethodArgumentResolver> customArgumentResolvers =
86+
new ArrayList<HandlerMethodArgumentResolver>();
8487

85-
private HttpMessageConverter<?>[] messageConverters;
88+
private final List<HandlerMethodReturnValueHandler> customReturnValueHandlers =
89+
new ArrayList<HandlerMethodReturnValueHandler>();
90+
91+
private List<HttpMessageConverter<?>> messageConverters;
8692

8793
private final Map<Class<?>, ExceptionMethodMapping> exceptionMethodMappingCache =
8894
new ConcurrentHashMap<Class<?>, ExceptionMethodMapping>();
@@ -99,95 +105,139 @@ public RequestMappingHandlerMethodExceptionResolver() {
99105
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
100106
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
101107

102-
this.messageConverters = new HttpMessageConverter[] { new ByteArrayHttpMessageConverter(),
103-
stringHttpMessageConverter, new SourceHttpMessageConverter<Source>(),
104-
new XmlAwareFormHttpMessageConverter() };
108+
messageConverters = new ArrayList<HttpMessageConverter<?>>();
109+
messageConverters.add(new ByteArrayHttpMessageConverter());
110+
messageConverters.add(stringHttpMessageConverter);
111+
messageConverters.add(new SourceHttpMessageConverter<Source>());
112+
messageConverters.add(new XmlAwareFormHttpMessageConverter());
105113
}
106114

107115
/**
108-
* Set one or more custom ArgumentResolvers to use for special method parameter types.
109-
* <p>Any such custom ArgumentResolver will kick in first, having a chance to resolve
110-
* an argument value before the standard argument handling kicks in.
111-
* <p>Note: this is provided for backward compatibility. The preferred way to do this is to
112-
* implement a {@link HandlerMethodArgumentResolver}.
116+
* Set one or more custom argument resolvers to use with {@link ExceptionHandler} methods. Custom argument resolvers
117+
* are given a chance to resolve argument values ahead of the standard argument resolvers registered by default.
118+
* <p>Argument resolvers of type {@link HandlerMethodArgumentResolver} and {@link WebArgumentResolver} are
119+
* accepted with instances of the latter adapted via {@link ServletWebArgumentResolverAdapter}. For new
120+
* implementations {@link HandlerMethodArgumentResolver} should be preferred over {@link WebArgumentResolver}.
113121
*/
114-
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
115-
this.customArgumentResolvers = argumentResolvers;
122+
public void setCustomArgumentResolvers(List<?> argumentResolvers) {
123+
if (argumentResolvers == null) {
124+
return;
125+
}
126+
List<HandlerMethodArgumentResolver> adaptedResolvers = new ArrayList<HandlerMethodArgumentResolver>();
127+
for (Object resolver : argumentResolvers) {
128+
if (resolver instanceof WebArgumentResolver) {
129+
adaptedResolvers.add(new ServletWebArgumentResolverAdapter((WebArgumentResolver) resolver));
130+
}
131+
else if (resolver instanceof HandlerMethodArgumentResolver) {
132+
adaptedResolvers.add((HandlerMethodArgumentResolver) resolver);
133+
}
134+
else {
135+
throw new IllegalArgumentException(
136+
"An argument resolver must be a HandlerMethodArgumentResolver or a WebArgumentResolver");
137+
}
138+
}
139+
this.customArgumentResolvers.addAll(adaptedResolvers);
140+
}
141+
142+
/**
143+
* Set the argument resolvers to use with {@link ExceptionHandler} methods.
144+
* This is an optional property providing full control over all argument resolvers in contrast to
145+
* {@link #setCustomArgumentResolvers(List)}, which does not override default registrations.
146+
* @param argumentResolvers argument resolvers for {@link ExceptionHandler} methods
147+
*/
148+
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
149+
if (argumentResolvers != null) {
150+
this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
151+
registerArgumentResolvers(argumentResolvers);
152+
}
116153
}
117154

118155
/**
119-
* Set the message body converters to use.
120-
* <p>These converters are used to convert from and to HTTP requests and responses.
156+
* Set custom return value handlers to use to handle the return values of {@link ExceptionHandler} methods.
157+
* Custom return value handlers are given a chance to handle a return value before the standard
158+
* return value handlers registered by default.
159+
* @param returnValueHandlers custom return value handlers for {@link ExceptionHandler} methods
121160
*/
122-
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
123-
this.messageConverters = messageConverters;
161+
public void setCustomReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
162+
if (returnValueHandlers != null) {
163+
this.customReturnValueHandlers.addAll(returnValueHandlers);
164+
}
124165
}
125166

126167
/**
127-
* Set the {@link HandlerMethodArgumentResolver}s to use to resolve argument values for
128-
* {@link ExceptionHandler} methods. This is an optional property.
129-
* @param argumentResolvers the argument resolvers to use
168+
* Set the {@link HandlerMethodReturnValueHandler}s to use to use with {@link ExceptionHandler} methods.
169+
* This is an optional property providing full control over all return value handlers in contrast to
170+
* {@link #setCustomReturnValueHandlers(List)}, which does not override default registrations.
171+
* @param returnValueHandlers the return value handlers for {@link ExceptionHandler} methods
130172
*/
131-
public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolver[] argumentResolvers) {
132-
this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
133-
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
134-
this.argumentResolvers.registerArgumentResolver(resolver);
173+
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
174+
if (returnValueHandlers != null) {
175+
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
176+
registerReturnValueHandlers(returnValueHandlers);
135177
}
136178
}
137179

138180
/**
139-
* Set the {@link HandlerMethodReturnValueHandler}s to use to handle the return values of
140-
* {@link ExceptionHandler} methods. This is an optional property.
141-
* @param returnValueHandlers the return value handlers to use
181+
* Set the message body converters to use.
182+
* <p>These converters are used to convert from and to HTTP requests and responses.
142183
*/
143-
public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandler[] returnValueHandlers) {
144-
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
145-
for (HandlerMethodReturnValueHandler handler : returnValueHandlers) {
146-
this.returnValueHandlers.registerReturnValueHandler(handler);
147-
}
184+
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
185+
this.messageConverters = messageConverters;
148186
}
149187

150188
public void afterPropertiesSet() throws Exception {
151-
initMethodArgumentResolvers();
152-
initMethodReturnValueHandlers();
189+
if (argumentResolvers == null) {
190+
argumentResolvers = new HandlerMethodArgumentResolverComposite();
191+
registerArgumentResolvers(customArgumentResolvers);
192+
registerArgumentResolvers(getDefaultArgumentResolvers());
193+
}
194+
if (returnValueHandlers == null) {
195+
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
196+
registerReturnValueHandlers(customReturnValueHandlers);
197+
registerReturnValueHandlers(getDefaultReturnValueHandlers(messageConverters));
198+
}
199+
}
200+
201+
private void registerArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
202+
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
203+
this.argumentResolvers.registerArgumentResolver(resolver);
204+
}
153205
}
154206

155-
private void initMethodArgumentResolvers() {
156-
if (argumentResolvers != null) {
157-
return;
207+
private void registerReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
208+
for (HandlerMethodReturnValueHandler handler : returnValueHandlers) {
209+
this.returnValueHandlers.registerReturnValueHandler(handler);
158210
}
159-
argumentResolvers = new HandlerMethodArgumentResolverComposite();
160-
161-
argumentResolvers.registerArgumentResolver(new ServletRequestMethodArgumentResolver());
162-
argumentResolvers.registerArgumentResolver(new ServletResponseMethodArgumentResolver());
163-
164-
if (customArgumentResolvers != null) {
165-
for (WebArgumentResolver customResolver : customArgumentResolvers) {
166-
argumentResolvers.registerArgumentResolver(new ServletWebArgumentResolverAdapter(customResolver));
167-
}
168-
}
169211
}
170212

171-
private void initMethodReturnValueHandlers() {
172-
if (returnValueHandlers != null) {
173-
return;
174-
}
175-
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
213+
public static List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
214+
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
215+
resolvers.add(new ServletRequestMethodArgumentResolver());
216+
resolvers.add(new ServletResponseMethodArgumentResolver());
217+
return resolvers;
218+
}
176219

220+
public static List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers(
221+
List<HttpMessageConverter<?>> messageConverters) {
222+
223+
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
224+
177225
// Annotation-based handlers
178-
returnValueHandlers.registerReturnValueHandler(new RequestResponseBodyMethodProcessor(messageConverters));
179-
returnValueHandlers.registerReturnValueHandler(new ModelAttributeMethodProcessor(false));
226+
handlers.add(new RequestResponseBodyMethodProcessor(messageConverters));
227+
handlers.add(new ModelAttributeMethodProcessor(false));
180228

181229
// Type-based handlers
182-
returnValueHandlers.registerReturnValueHandler(new ModelAndViewMethodReturnValueHandler());
183-
returnValueHandlers.registerReturnValueHandler(new ModelMethodProcessor());
184-
returnValueHandlers.registerReturnValueHandler(new ViewMethodReturnValueHandler());
185-
returnValueHandlers.registerReturnValueHandler(new HttpEntityMethodProcessor(messageConverters));
230+
handlers.add(new ModelAndViewMethodReturnValueHandler());
231+
handlers.add(new ModelMethodProcessor());
232+
handlers.add(new ViewMethodReturnValueHandler());
233+
handlers.add(new HttpEntityMethodProcessor(messageConverters));
186234

187235
// Default handler
188-
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(null));
236+
handlers.add(new DefaultMethodReturnValueHandler());
237+
238+
return handlers;
189239
}
190-
240+
191241
/**
192242
* Attempts to find an {@link ExceptionHandler}-annotated method that can handle the thrown exception.
193243
* The exception-handling method, if found, is invoked resulting in a {@link ModelAndView}.

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public abstract class AbstractMessageConverterMethodProcessor
4646

4747
protected final Log logger = LogFactory.getLog(getClass());
4848

49-
private final HttpMessageConverter<?>[] messageConverters;
49+
private final List<HttpMessageConverter<?>> messageConverters;
5050

51-
protected AbstractMessageConverterMethodProcessor(HttpMessageConverter<?>... messageConverters) {
51+
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
5252
Assert.notNull(messageConverters, "'messageConverters' must not be null");
5353
this.messageConverters = messageConverters;
5454
}

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/DefaultMethodReturnValueHandler.java

+18-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.web.servlet.mvc.method.annotation.support;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.ArrayList;
21+
import java.util.List;
2022

2123
import org.springframework.beans.BeanUtils;
2224
import org.springframework.core.MethodParameter;
@@ -41,10 +43,22 @@
4143
*/
4244
public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
4345

44-
private final ModelAndViewResolver[] customModelAndViewResolvers;
46+
private final List<ModelAndViewResolver> modelAndViewResolvers = new ArrayList<ModelAndViewResolver>();
4547

46-
public DefaultMethodReturnValueHandler(ModelAndViewResolver[] customResolvers) {
47-
this.customModelAndViewResolvers = (customResolvers != null) ? customResolvers : new ModelAndViewResolver[] {};
48+
/**
49+
* Create a {@link DefaultMethodReturnValueHandler} instance without {@link ModelAndViewResolver}s.
50+
*/
51+
public DefaultMethodReturnValueHandler() {
52+
this(null);
53+
}
54+
55+
/**
56+
* Create a {@link DefaultMethodReturnValueHandler} with a list of {@link ModelAndViewResolver}s.
57+
*/
58+
public DefaultMethodReturnValueHandler(List<ModelAndViewResolver> modelAndViewResolvers) {
59+
if (modelAndViewResolvers != null) {
60+
this.modelAndViewResolvers.addAll(modelAndViewResolvers);
61+
}
4862
}
4963

5064
public boolean supportsReturnType(MethodParameter returnType) {
@@ -60,7 +74,7 @@ public void handleReturnValue(Object returnValue,
6074
ModelAndViewContainer mavContainer,
6175
NativeWebRequest webRequest) throws Exception {
6276

63-
for (ModelAndViewResolver resolver : this.customModelAndViewResolvers) {
77+
for (ModelAndViewResolver resolver : modelAndViewResolvers) {
6478
Class<?> handlerType = returnType.getDeclaringClass();
6579
Method method = returnType.getMethod();
6680
ExtendedModelMap extModel = (ExtendedModelMap) mavContainer.getModel();

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.reflect.GenericArrayType;
2222
import java.lang.reflect.ParameterizedType;
2323
import java.lang.reflect.Type;
24+
import java.util.List;
2425

2526
import javax.servlet.http.HttpServletRequest;
2627
import javax.servlet.http.HttpServletResponse;
@@ -53,7 +54,7 @@
5354
*/
5455
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
5556

56-
public HttpEntityMethodProcessor(HttpMessageConverter<?>... messageConverters) {
57+
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
5758
super(messageConverters);
5859
}
5960

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.servlet.mvc.method.annotation.support;
1818

1919
import java.io.IOException;
20+
import java.util.List;
2021

2122
import javax.servlet.http.HttpServletRequest;
2223
import javax.servlet.http.HttpServletResponse;
@@ -47,7 +48,7 @@
4748
*/
4849
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
4950

50-
public RequestResponseBodyMethodProcessor(HttpMessageConverter<?>... messageConverters) {
51+
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
5152
super(messageConverters);
5253
}
5354

0 commit comments

Comments
 (0)