SPR-8214 Javadoc and polish

This commit is contained in:
Rossen Stoyanchev 2011-04-13 23:15:19 +00:00
parent 62d40dc7aa
commit aa065e8310
40 changed files with 466 additions and 420 deletions

View File

@ -24,7 +24,7 @@ import org.springframework.web.servlet.ModelAndView;
/** /**
* Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver} * Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
* implementations that support {@link HandlerMethod HandlerMethod}s. * implementations that support handling exceptions from {@link HandlerMethod}s rather than handlers.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1

View File

@ -26,10 +26,11 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.WebContentGenerator; import org.springframework.web.servlet.support.WebContentGenerator;
/** /**
* Abstract base class for {@link HandlerAdapter} implementations that support {@link HandlerMethod}s. * Abstract base class for {@link HandlerAdapter} implementations that support the handling of requests through
* Contains template methods for handling these handler method. * the execution of {@link HandlerMethod}s rather than handlers.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.1
*/ */
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered { public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

View File

@ -374,7 +374,7 @@ public class RequestMappingHandlerMethodAdapter extends AbstractHandlerMethodAda
resolvers.add(new RequestParamMethodArgumentResolver(beanFactory, false)); resolvers.add(new RequestParamMethodArgumentResolver(beanFactory, false));
resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver(beanFactory)); resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(messageConverters)); resolvers.add(new RequestResponseBodyMethodProcessor(messageConverters));
resolvers.add(new RequestHeaderMethodArgumentResolver(beanFactory)); resolvers.add(new RequestHeaderMethodArgumentResolver(beanFactory));
@ -404,7 +404,7 @@ public class RequestMappingHandlerMethodAdapter extends AbstractHandlerMethodAda
// Annotation-based resolvers // Annotation-based resolvers
resolvers.add(new RequestParamMethodArgumentResolver(beanFactory, false)); resolvers.add(new RequestParamMethodArgumentResolver(beanFactory, false));
resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver(beanFactory)); resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new ExpressionValueMethodArgumentResolver(beanFactory)); resolvers.add(new ExpressionValueMethodArgumentResolver(beanFactory));
// Type-based resolvers // Type-based resolvers

View File

@ -25,7 +25,7 @@ import org.springframework.web.method.annotation.InitBinderMethodDataBinderFacto
import org.springframework.web.method.support.InvocableHandlerMethod; import org.springframework.web.method.support.InvocableHandlerMethod;
/** /**
* An {@link InitBinderMethodDataBinderFactory} for Servlet environments. * An {@link InitBinderMethodDataBinderFactory} that creates a {@link ServletRequestDataBinder}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1

View File

@ -30,16 +30,19 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandlerCom
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.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
/** /**
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value through registered * Extends {@link InvocableHandlerMethod} with the ability to handle the value returned from the method through
* {@link HandlerMethodArgumentResolver}s. * a registered {@link HandlerMethodArgumentResolver} that supports the given return value type.
* Return value handling may include writing to the response or updating the {@link ModelAndViewContainer} structure.
* *
* <p>The {@link ModelAndViewContainer} for the request contains the results from the handling of the return value. * <p>If the underlying method has a {@link ResponseStatus} instruction, the status on the response is set
* It can be used to access model attributes and view selection and to check if view resolution is needed. * accordingly after the method is invoked but before the return value is handled.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
* @see #invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...)
*/ */
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
@ -65,25 +68,23 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
if (annotation != null) { if (annotation != null) {
this.responseStatus = annotation.value(); this.responseStatus = annotation.value();
this.responseReason = annotation.reason(); this.responseReason = annotation.reason();
} }
} }
/** /**
* Invokes the method and handles the return value through registered {@link HandlerMethodReturnValueHandler}s. * Invokes the method and handles the return value through a registered {@link HandlerMethodReturnValueHandler}.
* If the handler method is annotated with {@link ResponseStatus}, the status on the response is set accordingly * <p>Return value handling may be skipped entirely when the method returns {@code null} (also possibly due
* after method invocation but before return value handling. * to a {@code void} return type) and one of the following additional conditions is true:
* <p>Return value handling may be skipped entirely if the handler method returns a {@code null} (or is a
* {@code void} method) and one of the following other conditions is true:
* <ul> * <ul>
* <li>One of the {@link HandlerMethodArgumentResolver}s set the {@link ModelAndViewContainer#setResolveView(boolean)} * <li>A {@link HandlerMethodArgumentResolver} has set the {@link ModelAndViewContainer#setResolveView(boolean)}
* flag to {@code false}. This is the case when a method argument allows the handler method access to the response. * flag to {@code false} -- e.g. method arguments providing access to the response.
* <li>The request qualifies as being not modified according to {@link ServletWebRequest#isNotModified()}. * <li>The request qualifies as "not modified" as defined in {@link ServletWebRequest#checkNotModified(long)}
* This is used in conjunction with a "Last-Modified" header or ETag. * and {@link ServletWebRequest#checkNotModified(String)}. In this case a response with "not modified" response
* <li>The status on the response was set as a result of a {@link ResponseStatus} annotation * headers will be automatically generated without the need for return value handling.
* <li>The status on the response is set due to a @{@link ResponseStatus} instruction.
* </ul> * </ul>
* <p>After the call, use the {@link ModelAndViewContainer} parameter to access model attributes and view selection * <p>After the return value is handled, callers of this method can use the {@link ModelAndViewContainer}
* and to determine if view resolution is needed. * to gain access to model attributes, view selection choices, and to check if view resolution is even needed.
* *
* @param request the current request * @param request the current request
* @param mavContainer the {@link ModelAndViewContainer} for the current request * @param mavContainer the {@link ModelAndViewContainer} for the current request
@ -131,17 +132,18 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
} }
/** /**
* Does the request qualify as not modified? * Does the given request qualify as "not modified"?
* @see ServletWebRequest#checkNotModified(long)
* @see ServletWebRequest#checkNotModified(String)
*/ */
private boolean isRequestNotModified(NativeWebRequest request) { private boolean isRequestNotModified(NativeWebRequest request) {
return ((ServletWebRequest) request).isNotModified(); return ((ServletWebRequest) request).isNotModified();
} }
/** /**
* Does the method set the response status? * Does this method have the response status instruction?
*/ */
private boolean hasResponseStatus() { private boolean hasResponseStatus() {
return responseStatus != null; return responseStatus != null;
} }
} }

View File

@ -37,6 +37,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
/** /**
* A base class for resolving method argument values by reading from the body of a request with
* {@link HttpMessageConverter}s and for handling method return values by writing to the response with
* {@link HttpMessageConverter}s.
*
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1

View File

@ -31,12 +31,21 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
/** /**
* A catch-all {@link HandlerMethodReturnValueHandler} to handle return values not handled by any other return * Attempts to handle return value types not recognized by any other {@link HandlerMethodReturnValueHandler}.
* value handler. * Intended to be used as the last of a list of registered handlers as {@link #supportsReturnType(MethodParameter)}
* * always returns {@code true}.
* <p>This handler should always be last in the order as {@link #supportsReturnType(MethodParameter)} always returns * <p>Handling takes place in the following order:
* {@code true}. An attempt is made to handle the return value through a custom {@link ModelAndViewResolver}s or * <ul>
* otherwise by treating it as a single model attribute. * <li>Iterate over the list of {@link ModelAndViewResolver}s provided to the constructor of this class looking
* for a return value that isn't {@link ModelAndViewResolver#UNRESOLVED}.
* <li>If the return value is not a simple type it is treated as a single model attribute to be added to the model
* with a name derived from its type.
* </ul>
* <p>Note that {@link ModelAndViewResolver} is supported for backwards compatibility. Since the only way to check
* if it supports a return value type is to try to resolve the return value, a {@link ModelAndViewResolver} can
* only be invoked from here after no other {@link HandlerMethodReturnValueHandler} has recognized the return
* value. To avoid this limitation change the {@link ModelAndViewResolver} to implement
* {@link HandlerMethodReturnValueHandler} instead.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -65,10 +74,6 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
return true; return true;
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public void handleReturnValue(Object returnValue, public void handleReturnValue(Object returnValue,
MethodParameter returnType, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,

View File

@ -40,13 +40,11 @@ import org.springframework.util.Assert;
import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} and {@link HandlerMethodReturnValueHandler} * Resolves {@link HttpEntity} method argument values.
* that supports {@link HttpEntity} and {@link ResponseEntity}. * Handles {@link HttpEntity} and {@link ResponseEntity} return values.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -68,28 +66,17 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType); return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType);
} }
public boolean usesResponseArgument(MethodParameter parameterOrReturnType) {
// only when HttpEntity or ResponseEntity is used as a return type
return parameterOrReturnType.getParameterIndex() == -1;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException { throws IOException, HttpMediaTypeNotSupportedException {
HttpInputMessage inputMessage = createInputMessage(webRequest);
Class<?> paramType = getHttpEntityType(parameter); Class<?> paramType = getHttpEntityType(parameter);
Object body = readWithMessageConverters(webRequest, parameter, paramType); Object body = readWithMessageConverters(webRequest, parameter, paramType);
HttpInputMessage inputMessage = createInputMessage(webRequest);
return new HttpEntity<Object>(body, inputMessage.getHeaders()); return new HttpEntity<Object>(body, inputMessage.getHeaders());
} }
@Override
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
return new ServletServerHttpRequest(servletRequest);
}
private Class<?> getHttpEntityType(MethodParameter methodParam) { private Class<?> getHttpEntityType(MethodParameter methodParam) {
Assert.isAssignable(HttpEntity.class, methodParam.getParameterType()); Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType(); ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
@ -109,7 +96,12 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
"HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized"); "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
}
@Override
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
return new ServletServerHttpRequest(servletRequest);
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(Object returnValue,
@ -150,4 +142,5 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse(); HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
return new ServletServerHttpResponse(servletResponse); return new ServletServerHttpResponse(servletResponse);
} }
} }

View File

@ -23,7 +23,9 @@ import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
/** /**
* Handles {@link ModelAndView} return values. * Handles return values of type {@link ModelAndView} transferring their content to the {@link ModelAndViewContainer}.
* If the return value is {@code null}, the {@link ModelAndViewContainer#setResolveView(boolean)} flag is set to
* {@code false} to indicate view resolution is not needed.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -34,10 +36,6 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
return ModelAndView.class.isAssignableFrom(returnType.getParameterType()); return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public void handleReturnValue(Object returnValue, public void handleReturnValue(Object returnValue,
MethodParameter returnType, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,

View File

@ -20,19 +20,24 @@ import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver; import org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with * Resolves method arguments annotated with an @{@link PathVariable}.
* {@link PathVariable @PathVariable}. *
* <p>An @{@link PathVariable} is a named value that gets resolved from a URI template variable. It is always
* required and does not have a default value to fall back on. See the base class
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
*
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved path variable values that
* don't yet match the method parameter type.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Arjen Poutsma * @author Arjen Poutsma
@ -40,8 +45,8 @@ import org.springframework.web.servlet.HandlerMapping;
*/ */
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public PathVariableMethodArgumentResolver() {
super(beanFactory); super(null);
} }
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
@ -56,16 +61,16 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, MethodParameter parameter, String name) protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
throws Exception { String key = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Map<String, String> uriTemplateVariables = (Map<String, String>) webRequest.getAttribute( int scope = RequestAttributes.SCOPE_REQUEST;
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(key, scope);
return (uriTemplateVariables != null) ? uriTemplateVariables.get(name) : null; return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
} }
@Override @Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException { protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
throw new IllegalStateException("Could not find @PathVariable [" + name + "] in @RequestMapping"); throw new IllegalStateException("Could not find the URL template variable [" + name + "]");
} }
private static class PathVariableNamedValueInfo extends NamedValueInfo { private static class PathVariableNamedValueInfo extends NamedValueInfo {
@ -74,6 +79,4 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
super(annotation.value(), true, ValueConstants.DEFAULT_NONE); super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
} }
} }
} }

View File

@ -34,13 +34,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} and {@link HandlerMethodReturnValueHandler} that supports * Resolves method arguments annotated with @{@link RequestBody}.
* parameters annotated with {@link RequestBody} and return values annotated with {@link ResponseBody}. * Handles return values from methods annotated with @{@link ResponseBody}.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -60,11 +58,6 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
return returnType.getMethodAnnotation(ResponseBody.class) != null; return returnType.getMethodAnnotation(ResponseBody.class) != null;
} }
public boolean usesResponseArgument(MethodParameter parameterOrReturnType) {
return parameterOrReturnType.getParameterIndex() == -1 &&
parameterOrReturnType.getMethodAnnotation(ResponseBody.class) != null;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,

View File

@ -22,32 +22,38 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.support.CookieValueMethodArgumentResolver; import org.springframework.web.method.annotation.support.AbstractCookieValueMethodArgumentResolver;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
/** /**
* A {@link CookieValueMethodArgumentResolver} for Servlet environments. * A {@link AbstractCookieValueMethodArgumentResolver} that resolves the cookie value through the {@link HttpServletRequest}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class ServletCookieValueMethodArgumentResolver extends CookieValueMethodArgumentResolver { public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValueMethodArgumentResolver {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
public ServletCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public ServletCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
super(beanFactory); super(beanFactory);
} }
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
this.urlPathHelper = urlPathHelper;
}
@Override @Override
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest)
MethodParameter parameter, throws Exception {
String cookieName) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName); Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
if (Cookie.class.isAssignableFrom(parameter.getParameterType())) { if (Cookie.class.isAssignableFrom(parameter.getParameterType())) {
return cookieValue; return cookieValue;
} }
else if (cookieValue != null) { else if (cookieValue != null) {
return getUrlPathHelper().decodeRequestString(servletRequest, cookieValue.getValue()); return this.urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
} }
else { else {
return null; return null;

View File

@ -21,11 +21,13 @@ import javax.servlet.ServletRequest;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor; import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
/** /**
* A {@link ModelAttributeMethodProcessor} for Servlet environments. * A Servlet-specific {@link ModelAttributeMethodProcessor} variant that casts the {@link WebDataBinder}
* instance to {@link ServletRequestDataBinder} prior to invoking data binding.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -33,13 +35,12 @@ import org.springframework.web.method.annotation.support.ModelAttributeMethodPro
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor { public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
/** /**
* Creates a {@link ServletModelAttributeMethodProcessor} instance. * @param useDefaultResolution in default resolution mode a method argument that isn't a simple type, as
* @param resolveWithoutAnnotations enable default resolution mode in which parameters without * defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a model attribute even if it doesn't
* annotations that aren't simple types (see {@link BeanUtils#isSimpleProperty(Class)}) * have an @{@link ModelAttribute} annotation with its name derived from the model attribute type.
* are also treated as model attributes with a default name based on the model attribute type.
*/ */
public ServletModelAttributeMethodProcessor(boolean resolveWithoutAnnotations) { public ServletModelAttributeMethodProcessor(boolean useDefaultResolution) {
super(resolveWithoutAnnotations); super(useDefaultResolution);
} }
/** /**

View File

@ -36,61 +36,74 @@ import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.support.RequestContextUtils;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletRequest} and related arguments. * Resolves request-related method argument values of the following types:
* <ul>
* <li>{@link WebRequest}
* <li>{@link ServletRequest}
* <li>{@link MultipartRequest}
* <li>{@link HttpSession}
* <li>{@link Principal}
* <li>{@link Locale}
* <li>{@link InputStream}
* <li>{@link Reader}
* </ul>
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/ */
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver { public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
return WebRequest.class.isAssignableFrom(parameterType) || return WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(parameterType) || ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(parameterType) || Principal.class.isAssignableFrom(parameterType) || HttpSession.class.isAssignableFrom(paramType) ||
Locale.class.equals(parameterType) || InputStream.class.isAssignableFrom(parameterType) || Principal.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(parameterType); Locale.class.equals(paramType) ||
} InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType);
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException { WebDataBinderFactory binderFactory) throws IOException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
Class<?> parameterType = parameter.getParameterType();
if (WebRequest.class.isAssignableFrom(parameterType)) { Class<?> paramType = parameter.getParameterType();
if (WebRequest.class.isAssignableFrom(paramType)) {
return webRequest; return webRequest;
} }
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
Object nativeRequest = webRequest.getNativeRequest(parameterType); if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
Object nativeRequest = webRequest.getNativeRequest(paramType);
if (nativeRequest == null) { if (nativeRequest == null) {
throw new IllegalStateException( throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request); "Current request is not of type [" + paramType.getName() + "]: " + request);
} }
return nativeRequest; return nativeRequest;
} }
else if (HttpSession.class.isAssignableFrom(parameterType)) { else if (HttpSession.class.isAssignableFrom(paramType)) {
return request.getSession(); return request.getSession();
} }
else if (Principal.class.isAssignableFrom(parameterType)) { else if (Principal.class.isAssignableFrom(paramType)) {
return request.getUserPrincipal(); return request.getUserPrincipal();
} }
else if (Locale.class.equals(parameterType)) { else if (Locale.class.equals(paramType)) {
return RequestContextUtils.getLocale(request); return RequestContextUtils.getLocale(request);
} }
else if (InputStream.class.isAssignableFrom(parameterType)) { else if (InputStream.class.isAssignableFrom(paramType)) {
return request.getInputStream(); return request.getInputStream();
} }
else if (Reader.class.isAssignableFrom(parameterType)) { else if (Reader.class.isAssignableFrom(paramType)) {
return request.getReader(); return request.getReader();
} }
// should not happen else {
throw new UnsupportedOperationException(); // should not happen
throw new UnsupportedOperationException();
}
} }
} }

View File

@ -28,33 +28,46 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletResponse} and related arguments. * Resolves response-related method argument values of types:
* <ul>
* <li>{@link ServletResponse}
* <li>{@link OutputStream}
* <li>{@link Writer}
* </ul>
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/ */
public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver { public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
return ServletResponse.class.isAssignableFrom(parameterType) || return ServletResponse.class.isAssignableFrom(paramType)
OutputStream.class.isAssignableFrom(parameterType) || Writer.class.isAssignableFrom(parameterType); || OutputStream.class.isAssignableFrom(paramType)
} || Writer.class.isAssignableFrom(paramType);
public boolean usesResponseArgument(MethodParameter parameter) {
return true;
} }
/**
* {@inheritDoc}
* <p>Sets the {@link ModelAndViewContainer#setResolveView(boolean)} flag to {@code false} to indicate
* that the method signature provides access to the response. If subsequently the underlying method
* returns {@code null}, view resolution will be bypassed.
* @see ServletInvocableHandlerMethod#invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...)
*/
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException { WebDataBinderFactory binderFactory) throws IOException {
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> parameterType = parameter.getParameterType();
mavContainer.setResolveView(false); mavContainer.setResolveView(false);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> parameterType = parameter.getParameterType();
if (ServletResponse.class.isAssignableFrom(parameterType)) { if (ServletResponse.class.isAssignableFrom(parameterType)) {
Object nativeResponse = webRequest.getNativeResponse(parameterType); Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) { if (nativeResponse == null) {
@ -74,4 +87,5 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
} }

View File

@ -22,15 +22,16 @@ import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter; import org.springframework.web.method.annotation.support.AbstractWebArgumentResolverAdapter;
/** /**
* A Servlet-specific {@link WebArgumentResolverAdapter} that provides access to a {@link NativeWebRequest}. * A Servlet-specific {@link AbstractWebArgumentResolverAdapter} that creates a {@link NativeWebRequest}
* from {@link ServletRequestAttributes}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class ServletWebArgumentResolverAdapter extends WebArgumentResolverAdapter { public class ServletWebArgumentResolverAdapter extends AbstractWebArgumentResolverAdapter {
public ServletWebArgumentResolverAdapter(WebArgumentResolver adaptee) { public ServletWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
super(adaptee); super(adaptee);
@ -45,5 +46,4 @@ public class ServletWebArgumentResolverAdapter extends WebArgumentResolverAdapte
} }
return null; return null;
} }
} }

View File

@ -18,12 +18,16 @@ package org.springframework.web.servlet.mvc.method.annotation.support;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
/** /**
* Handles {@link View} and view name return values. * Handles return values that are of type {@link View} or {@link String} (i.e. a logical view name).
* <p>Since {@link String} return value can be interpeted in multiple ways, this resolver should be ordered
* after return value handlers that recognize annotated return values such as the
* {@link ModelAttributeMethodProcessor} and the {@link RequestResponseBodyMethodProcessor}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -35,10 +39,6 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
return (View.class.isAssignableFrom(paramType) || (String.class.equals(paramType))); return (View.class.isAssignableFrom(paramType) || (String.class.equals(paramType)));
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public void handleReturnValue(Object returnValue, public void handleReturnValue(Object returnValue,
MethodParameter returnType, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,

View File

@ -122,15 +122,6 @@ public class HttpEntityMethodProcessorTests {
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue)); assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue));
} }
@Test
public void usesResponseArgument() {
assertFalse("HttpEntity parameter uses response argument", processor.usesResponseArgument(httpEntityParam));
assertTrue("ResponseBody return type does not use response argument",
processor.usesResponseArgument(responseEntityReturnValue));
assertTrue("HttpEntity return type does not use response argument",
processor.usesResponseArgument(httpEntityReturnValue));
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void resolveArgument() throws Exception { public void resolveArgument() throws Exception {
@ -146,8 +137,9 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter); replay(messageConverter);
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, mavContainer, request, null); HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, mavContainer, request, null);
assertEquals("Invalid argument", expected, result.getBody());
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
assertEquals("Invalid argument", expected, result.getBody());
verify(messageConverter); verify(messageConverter);
} }
@ -164,6 +156,7 @@ public class HttpEntityMethodProcessorTests {
processor.resolveArgument(httpEntityParam, mavContainer, request, null); processor.resolveArgument(httpEntityParam, mavContainer, request, null);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
verify(messageConverter); verify(messageConverter);
} }

View File

@ -52,7 +52,7 @@ public class PathVariableMethodArgumentResolverTests {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
resolver = new PathVariableMethodArgumentResolver(null); resolver = new PathVariableMethodArgumentResolver();
Method method = getClass().getMethod("handle", String.class, String.class); Method method = getClass().getMethod("handle", String.class, String.class);
pathVarParam = new MethodParameter(method, 0); pathVarParam = new MethodParameter(method, 0);
stringParam = new MethodParameter(method, 1); stringParam = new MethodParameter(method, 1);
@ -62,11 +62,6 @@ public class PathVariableMethodArgumentResolverTests {
webRequest = new ServletWebRequest(servletRequest, servletResponse); webRequest = new ServletWebRequest(servletRequest, servletResponse);
} }
@Test
public void usesResponseArgument() {
assertFalse(resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("Parameter with @PathVariable annotation", resolver.supportsParameter(pathVarParam)); assertTrue("Parameter with @PathVariable annotation", resolver.supportsParameter(pathVarParam));

View File

@ -106,13 +106,6 @@ public class RequestResponseBodyMethodProcessorTests {
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue)); assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue));
} }
@Test
public void usesResponseArgument() {
assertFalse("RequestBody parameter uses response argument", processor.usesResponseArgument(stringParameter));
assertTrue("ResponseBody return type does not use response argument",
processor.usesResponseArgument(stringReturnValue));
}
@Test @Test
public void resolveArgument() throws Exception { public void resolveArgument() throws Exception {
MediaType contentType = MediaType.TEXT_PLAIN; MediaType contentType = MediaType.TEXT_PLAIN;
@ -127,8 +120,9 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter); replay(messageConverter);
Object result = processor.resolveArgument(stringParameter, mavContainer, webRequest, null); Object result = processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
assertEquals("Invalid argument", expected, result);
assertEquals("Invalid argument", expected, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
verify(messageConverter); verify(messageConverter);
} }
@ -146,6 +140,7 @@ public class RequestResponseBodyMethodProcessorTests {
processor.resolveArgument(stringParameter, mavContainer, webRequest, null); processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
verify(messageConverter); verify(messageConverter);
} }
@ -168,7 +163,7 @@ public class RequestResponseBodyMethodProcessorTests {
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest); processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertFalse("The ResolveView flag wasn't turned off", mavContainer.isResolveView());
verify(messageConverter); verify(messageConverter);
} }
@ -186,7 +181,7 @@ public class RequestResponseBodyMethodProcessorTests {
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest); processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertFalse("The ResolveView flag wasn't turned off", mavContainer.isResolveView());
verify(messageConverter); verify(messageConverter);
} }

View File

@ -31,7 +31,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.CookieValueMethodArgumentResolver; import org.springframework.web.method.annotation.support.AbstractCookieValueMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver;
/** /**
@ -39,7 +39,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletCook
*/ */
public class ServletCookieValueMethodArgumentResolverTests { public class ServletCookieValueMethodArgumentResolverTests {
private CookieValueMethodArgumentResolver resolver; private ServletCookieValueMethodArgumentResolver resolver;
private MethodParameter cookieParameter; private MethodParameter cookieParameter;
@ -65,11 +65,6 @@ public class ServletCookieValueMethodArgumentResolverTests {
} }
@Test
public void usesResponseArgument() throws NoSuchMethodException {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("Cookie parameter not supported", resolver.supportsParameter(cookieParameter)); assertTrue("Cookie parameter not supported", resolver.supportsParameter(cookieParameter));

View File

@ -33,6 +33,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockHttpSession;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
@ -47,6 +48,8 @@ public class ServletRequestMethodArgumentResolverTests {
private Method supportedParams; private Method supportedParams;
private ModelAndViewContainer mavContainer;
private ServletWebRequest webRequest; private ServletWebRequest webRequest;
private MockHttpServletRequest servletRequest; private MockHttpServletRequest servletRequest;
@ -57,23 +60,21 @@ public class ServletRequestMethodArgumentResolverTests {
supportedParams = getClass() supportedParams = getClass()
.getMethod("supportedParams", ServletRequest.class, MultipartRequest.class, HttpSession.class, .getMethod("supportedParams", ServletRequest.class, MultipartRequest.class, HttpSession.class,
Principal.class, Locale.class, InputStream.class, Reader.class, WebRequest.class); Principal.class, Locale.class, InputStream.class, Reader.class, WebRequest.class);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest(); servletRequest = new MockHttpServletRequest();
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse()); webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
} }
@Test
public void usesResponseArgument() {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void servletRequest() throws Exception { public void servletRequest() throws Exception {
MethodParameter servletRequestParameter = new MethodParameter(supportedParams, 0); MethodParameter servletRequestParameter = new MethodParameter(supportedParams, 0);
assertTrue("ServletRequest not supported", resolver.supportsParameter(servletRequestParameter)); boolean isSupported = resolver.supportsParameter(servletRequestParameter);
Object result = resolver.resolveArgument(servletRequestParameter, mavContainer, webRequest, null);
Object result = resolver.resolveArgument(servletRequestParameter, null, webRequest, null); assertTrue("ServletRequest not supported", isSupported);
assertSame("Invalid result", servletRequest, result); assertSame("Invalid result", servletRequest, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
} }
@Test @Test
@ -82,10 +83,12 @@ public class ServletRequestMethodArgumentResolverTests {
servletRequest.setSession(session); servletRequest.setSession(session);
MethodParameter sessionParameter = new MethodParameter(supportedParams, 2); MethodParameter sessionParameter = new MethodParameter(supportedParams, 2);
assertTrue("Session not supported", resolver.supportsParameter(sessionParameter)); boolean isSupported = resolver.supportsParameter(sessionParameter);
Object result = resolver.resolveArgument(sessionParameter, mavContainer, webRequest, null);
Object result = resolver.resolveArgument(sessionParameter, null, webRequest, null); assertTrue("Session not supported", isSupported);
assertSame("Invalid result", session, result); assertSame("Invalid result", session, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView());
} }
@Test @Test

View File

@ -57,11 +57,6 @@ public class ServletResponseMethodArgumentResolverTests {
webRequest = new ServletWebRequest(new MockHttpServletRequest(), servletResponse); webRequest = new ServletWebRequest(new MockHttpServletRequest(), servletResponse);
} }
@Test
public void usesResponseArgument() {
assertTrue("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void servletResponse() throws Exception { public void servletResponse() throws Exception {
MethodParameter servletResponseParameter = new MethodParameter(supportedParams, 0); MethodParameter servletResponseParameter = new MethodParameter(supportedParams, 0);

View File

@ -18,33 +18,34 @@ package org.springframework.web.method.annotation.support;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.util.UrlPathHelper;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with * A base abstract class to resolve method arguments annotated with @{@link CookieValue}. Subclasses must define how
* {@link CookieValue @CookieValue}. * to extract the cookie value from the request.
*
* <p>An @{@link CookieValue} is a named value that is resolved from a cookie. It has a required flag and a
* default value to fall back on when the cookie does not exist. See the base class
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
*
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved cookie values that don't yet match
* the method parameter type.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/ */
public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
private UrlPathHelper urlPathHelper = new UrlPathHelper(); /**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { * in default values, or {@code null} if default values are not expected to contain expressions
*/
public AbstractCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
super(beanFactory); super(beanFactory);
} }
public UrlPathHelper getUrlPathHelper() {
return urlPathHelper;
}
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
this.urlPathHelper = urlPathHelper;
}
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CookieValue.class); return parameter.hasParameterAnnotation(CookieValue.class);
} }
@ -55,18 +56,11 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodA
return new CookieValueNamedValueInfo(annotation); return new CookieValueNamedValueInfo(annotation);
} }
@Override
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
MethodParameter parameter,
String cookieName) throws Exception {
throw new UnsupportedOperationException("@CookieValue not supported");
}
@Override @Override
protected void handleMissingValue(String cookieName, MethodParameter parameter) { protected void handleMissingValue(String cookieName, MethodParameter parameter) {
String paramTypeName = parameter.getParameterType().getName();
throw new IllegalStateException( throw new IllegalStateException(
"Missing cookie value '" + cookieName + "' of type [" + parameter.getParameterType().getName() + "]"); "Missing cookie named '" + cookieName + "' for method parameter type [" + paramTypeName + "]");
} }
private static class CookieValueNamedValueInfo extends NamedValueInfo { private static class CookieValueNamedValueInfo extends NamedValueInfo {

View File

@ -25,6 +25,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
@ -34,7 +35,18 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Abstract base class for argument resolvers that resolve named values. * Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and
* path variables are examples of named values. Each may have a name, a required flag, and a default value.
* <p>Subclasses define how to do the following:
* <ul>
* <li>Obtain named value information for a method parameter
* <li>Resolve names into argument values
* <li>Handle missing argument values when argument values are required
* </ul>
* <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions.
* For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor.
* <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't
* match the method parameter type.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -49,15 +61,15 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
private Map<MethodParameter, NamedValueInfo> namedValueInfoCache = private Map<MethodParameter, NamedValueInfo> namedValueInfoCache =
new ConcurrentHashMap<MethodParameter, NamedValueInfo>(); new ConcurrentHashMap<MethodParameter, NamedValueInfo>();
/**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* in default values, or {@code null} if default values are not expected to contain expressions
*/
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory; this.beanFactory = beanFactory;
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null; this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public final Object resolveArgument(MethodParameter parameter, public final Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
@ -66,7 +78,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
Object arg = resolveNamedValueArgument(webRequest, parameter, namedValueInfo.name); Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
if (arg == null) { if (arg == null) {
if (namedValueInfo.defaultValue != null) { if (namedValueInfo.defaultValue != null) {
@ -87,33 +99,22 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
} }
} }
/**
* Obtain the named value for the given method parameter.
*/
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo result = namedValueInfoCache.get(parameter); NamedValueInfo namedValueInfo = namedValueInfoCache.get(parameter);
if (result == null) { if (namedValueInfo == null) {
NamedValueInfo info = createNamedValueInfo(parameter); namedValueInfo = createNamedValueInfo(parameter);
String name = info.name; namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
if (name.length() == 0) { namedValueInfoCache.put(parameter, namedValueInfo);
name = parameter.getParameterName();
if (name == null) {
throw new IllegalStateException("No parameter name specified for argument of type [" +
parameter.getParameterType().getName() +
"], and no parameter name information found in class file either.");
}
}
boolean required = info.required;
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
result = new NamedValueInfo(name, required, defaultValue);
namedValueInfoCache.put(parameter, result);
} }
return result; return namedValueInfo;
} }
/** /**
* Creates a new {@link NamedValueInfo} object for the given method parameter. * Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
* * retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}.
* <p>Implementations typically retrieve the method annotation by means of {@link
* MethodParameter#getParameterAnnotation(Class)}.
* *
* @param parameter the method parameter * @param parameter the method parameter
* @return the named value information * @return the named value information
@ -121,40 +122,50 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter); protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
/** /**
* Resolves the given parameter into a method argument. * Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values.
*/
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.length() == 0) {
name = parameter.getParameterName();
Assert.notNull(name, "Name for argument type [" + parameter.getParameterType().getName()
+ "] not available, and parameter name information not found in class file either.");
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
/**
* Resolves the given parameter type and value name into an argument value.
* @param name the name of the value being resolved
* @param parameter the method parameter to resolve to an argument value
* @param request the current request
* *
* @param webRequest the current web request, allowing access to the native request as well * @return the resolved argument. May be {@code null}
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to the
* {@link #supportsParameter(org.springframework.core.MethodParameter)} method of this interface, which must have
* returned {@code true}.
* @param name the name
* @return the resolved argument. May be {@code null}.
* @throws Exception in case of errors * @throws Exception in case of errors
*/ */
protected abstract Object resolveNamedValueArgument(NativeWebRequest webRequest, protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
MethodParameter parameter, throws Exception;
String name) throws Exception;
private Object resolveDefaultValue(String value) { /**
* Resolves the given default value into an argument value.
*/
private Object resolveDefaultValue(String defaultValue) {
if (beanFactory == null) { if (beanFactory == null) {
return value; return defaultValue;
} }
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value); String placeholdersResolved = beanFactory.resolveEmbeddedValue(defaultValue);
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver(); BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
if (exprResolver == null) { if (exprResolver == null) {
return value; return defaultValue;
} }
return exprResolver.evaluate(placeholdersResolved, expressionContext); return exprResolver.evaluate(placeholdersResolved, expressionContext);
} }
/** /**
* Invoked when a named value is required, but * Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
* {@link #resolveNamedValueArgument(NativeWebRequest, MethodParameter, String)} returned {@code null} * returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
* and there is no default value set. * @param name the name for the value
*
* <p>Concrete subclasses typically throw an exception in this scenario.
*
* @param name the name
* @param parameter the method parameter * @param parameter the method parameter
*/ */
protected abstract void handleMissingValue(String name, MethodParameter parameter) throws ServletException; protected abstract void handleMissingValue(String name, MethodParameter parameter) throws ServletException;
@ -189,7 +200,5 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.required = required; this.required = required;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
} }
} }

View File

@ -21,32 +21,48 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebArgumentResolver; import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Adapts a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver} contract. * An abstract base class adapting a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver}
* contract. Provided for backwards compatibility, some important considerations are listed below.
*
* <p>The method {@link #supportsParameter(MethodParameter)} is implemented by trying to resolve the value through
* the {@link WebArgumentResolver} and verifying the resulting value is not {@link WebArgumentResolver#UNRESOLVED}.
* Exceptions resulting from that are absorbed and ignored since the adapter can't be sure if this is the resolver
* that supports the method parameter or not. To avoid this limitation change the {@link WebArgumentResolver} to
* implement the {@link HandlerMethodArgumentResolver} contract instead.
*
* <p>Another potentially useful advantage of {@link HandlerMethodArgumentResolver} is that it provides access to
* model attributes through the {@link ModelAndViewContainer} as well as access to a {@link WebDataBinderFactory}
* for when type conversion through a {@link WebDataBinder} is needed.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver { public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
private final Log logger = LogFactory.getLog(this.getClass()); private final Log logger = LogFactory.getLog(this.getClass());
private final WebArgumentResolver adaptee; private final WebArgumentResolver adaptee;
public WebArgumentResolverAdapter(WebArgumentResolver adaptee) { /**
* Create a {@link AbstractWebArgumentResolverAdapter} with the {@link WebArgumentResolver} instance to delegate to.
*/
public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
Assert.notNull(adaptee, "'adaptee' must not be null"); Assert.notNull(adaptee, "'adaptee' must not be null");
this.adaptee = adaptee; this.adaptee = adaptee;
} }
/**
* See the class-level documentation for an important consideration about exceptions arising in this method.
*/
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
try { try {
NativeWebRequest webRequest = getWebRequest(); NativeWebRequest webRequest = getWebRequest();
@ -65,15 +81,16 @@ public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver
} }
} }
protected NativeWebRequest getWebRequest() { /**
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); * Provide access to a {@link NativeWebRequest}.
return (requestAttributes instanceof NativeWebRequest) ? (NativeWebRequest) requestAttributes : null; */
} protected abstract NativeWebRequest getWebRequest();
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
/**
* Resolves the argument value by delegating to the {@link WebArgumentResolver} instance.
* @exception IllegalStateException if the resolved value is {@link WebArgumentResolver#UNRESOLVED} or if the
* return value type cannot be assigned to the method parameter type.
*/
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,

View File

@ -23,18 +23,19 @@ import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* An implementation of {@link HandlerMethodArgumentResolver} that resolves {@link Errors} method parameters. * Resolves method arguments of type {@link Errors} and {@link BindingResult}.
* Such parameters must be preceded by {@link ModelAttribute} parameters as described in {@link RequestMapping}. *
* <p>This argument should appear after a model attribute argument in the signature of the handler method.
* It is resolved by accessing the last attribute in the model expecting that to be a {@link BindingResult}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1
*/ */
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver { public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver {
@ -43,10 +44,6 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
return Errors.class.isAssignableFrom(paramType); return Errors.class.isAssignableFrom(paramType);
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
@ -67,5 +64,4 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
private boolean isBindingResultKey(String key) { private boolean isBindingResultKey(String key) {
return key.startsWith(BindingResult.MODEL_KEY_PREFIX); return key.startsWith(BindingResult.MODEL_KEY_PREFIX);
} }
} }

View File

@ -21,18 +21,28 @@ import javax.servlet.ServletException;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated * Resolves method arguments annotated with @{@link Value}.
* with {@link Value @Value}. *
* <p>An @{@link Value} is a named value that does not have a name but gets resolved from a default value string
* that may contain ${...} placeholder or Spring Expression Language #{...} expressions. See the base class
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
*
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved argument values that don't yet match
* the method parameter type.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* in default values, or {@code null} if default values are not expected to contain expressions
*/
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
super(beanFactory); super(beanFactory);
} }
@ -48,16 +58,15 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
} }
@Override @Override
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, MethodParameter parameter, String name) protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest)
throws Exception { throws Exception {
// Only interested in default value resolution // There is no name to be resolved
return null; return null;
} }
@Override @Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException { protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
// Should not happen throw new UnsupportedOperationException("Did not expect to handle a missing value: an @Value is never required");
throw new UnsupportedOperationException();
} }
private static class ExpressionValueNamedValueInfo extends NamedValueInfo { private static class ExpressionValueNamedValueInfo extends NamedValueInfo {
@ -66,5 +75,4 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
super("@Value", false, annotation.value()); super("@Value", false, annotation.value());
} }
} }
} }

View File

@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder; import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
@ -34,24 +35,30 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves model attribute method parameters. * Resolves method arguments annotated with @{@link ModelAttribute}. Or if created in default resolution mode,
* resolves any non-simple type argument even without an @{@link ModelAttribute}. See the constructor for details.
*
* <p>A model attribute argument value is obtained from the model or is created using its default constructor.
* Data binding and optionally validation is then applied through a {@link WebDataBinder} instance. Validation is
* invoked optionally when the argument is annotated with an {@code @Valid}.
*
* <p>Also handles return values from methods annotated with an @{@link ModelAttribute}. The return value is
* added to the {@link ModelAndViewContainer}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class ModelAttributeMethodProcessor public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
private final boolean resolveArgumentsWithoutAnnotations; private final boolean useDefaultResolution;
/** /**
* Creates a {@link ModelAttributeMethodProcessor} instance. * @param useDefaultResolution in default resolution mode a method argument that isn't a simple type, as
* @param resolveArgumentsWithoutAnnotations enable default resolution mode in which arguments without * defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a model attribute even if it doesn't
* annotations that aren't simple types (see {@link BeanUtils#isSimpleProperty(Class)}) * have an @{@link ModelAttribute} annotation with its name derived from the model attribute type.
* are also treated as model attributes with a default name based on the model attribute type.
*/ */
public ModelAttributeMethodProcessor(boolean resolveArgumentsWithoutAnnotations) { public ModelAttributeMethodProcessor(boolean useDefaultResolution) {
this.resolveArgumentsWithoutAnnotations = resolveArgumentsWithoutAnnotations; this.useDefaultResolution = useDefaultResolution;
} }
/** /**
@ -62,7 +69,7 @@ public class ModelAttributeMethodProcessor
if (parameter.hasParameterAnnotation(ModelAttribute.class)) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
return true; return true;
} }
else if (this.resolveArgumentsWithoutAnnotations) { else if (this.useDefaultResolution) {
return !BeanUtils.isSimpleProperty(parameter.getParameterType()); return !BeanUtils.isSimpleProperty(parameter.getParameterType());
} }
else { else {
@ -70,15 +77,13 @@ public class ModelAttributeMethodProcessor
} }
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
/** /**
* Resolves the argument to a model attribute creating a {@link WebDataBinder} and invoking data binding on it. * Resolves the argument to a model attribute looking up the attribute in the model or instantiating it using its
* The model attribute is obtained from the model first or otherwise created via direct instantiation. * default constructor. Data binding and optionally validation is then applied through a {@link WebDataBinder}
* @throws Exception if data binder initialization fails or if data binding results in errors and the next * instance. Validation is invoked optionally when the method parameter is annotated with an {@code @Valid}.
* method parameter is not of type {@link Errors}. *
* @throws Exception if a {@link WebDataBinder} could not be created or if data binding and validation result in
* an error and the next method parameter is not of type {@link Errors} or {@link BindingResult}.
*/ */
public final Object resolveArgument(MethodParameter parameter, public final Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
@ -164,5 +169,4 @@ public class ModelAttributeMethodProcessor
mavContainer.addAttribute(name, returnValue); mavContainer.addAttribute(name, returnValue);
} }
} }
} }

View File

@ -28,7 +28,12 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Model} and {@link Map} method parameters. * Resolves {@link Map} and {@link Model} method arguments.
*
* <p>Handles {@link Model} return values adding their attributes to the {@link ModelAndViewContainer}.
* Handles {@link Map} return values in the same way as long as the method does not have an @{@link ModelAttribute}.
* If the method does have an @{@link ModelAttribute}, it is assumed the returned {@link Map} is a model attribute
* and not a model.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -40,10 +45,6 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
return Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType); return Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType);
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
@ -78,5 +79,4 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
} }

View File

@ -31,10 +31,17 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with * Resolves {@link Map} method arguments annotated with an @{@link RequestHeader}.
* {@link RequestHeader @RequestHeader}. * See {@link RequestHeaderMethodArgumentResolver} for individual header values with an @{@link RequestHeader}.
*
* <p>The created {@link Map} contains all request header name/value pairs. If the method parameter type
* is {@link MultiValueMap} instead, the created map contains all request headers and all their values in case
* request headers have multiple values.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
* @see RequestHeaderMethodArgumentResolver
*/ */
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver { public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@ -43,10 +50,6 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
&& Map.class.isAssignableFrom(parameter.getParameterType()); && Map.class.isAssignableFrom(parameter.getParameterType());
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,

View File

@ -20,18 +20,31 @@ import java.util.Map;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with * Resolves method arguments annotated with @{@link RequestHeader} with the exception of {@link Map} arguments.
* {@link RequestHeader @RequestHeader}. * See {@link RequestHeaderMapMethodArgumentResolver} for {@link Map} arguments annotated with @{@link RequestHeader}.
*
* <p>An @{@link RequestHeader} is a named value that gets resolved from a request header. It has a required flag
* and a default value to fall back on when the request header does not exist. See the base class
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
*
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved request header values that
* don't yet match the method parameter type.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/ */
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* in default values, or {@code null} if default values are not expected to contain expressions
*/
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
super(beanFactory); super(beanFactory);
} }
@ -48,10 +61,8 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
} }
@Override @Override
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
MethodParameter parameter, String[] headerValues = request.getHeaderValues(name);
String headerName) throws Exception {
String[] headerValues = webRequest.getHeaderValues(headerName);
if (headerValues != null) { if (headerValues != null) {
return (headerValues.length == 1 ? headerValues[0] : headerValues); return (headerValues.length == 1 ? headerValues[0] : headerValues);
} }
@ -62,8 +73,9 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
@Override @Override
protected void handleMissingValue(String headerName, MethodParameter parameter) { protected void handleMissingValue(String headerName, MethodParameter parameter) {
String paramTypeName = parameter.getParameterType().getName();
throw new IllegalStateException( throw new IllegalStateException(
"Missing header '" + headerName + "' of type [" + parameter.getParameterType().getName() + "]"); "Missing header '" + headerName + "' for method parameter type [" + paramTypeName + "]");
} }
private static class RequestHeaderNamedValueInfo extends NamedValueInfo { private static class RequestHeaderNamedValueInfo extends NamedValueInfo {
@ -72,5 +84,4 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
super(annotation.value(), annotation.required(), annotation.defaultValue()); super(annotation.value(), annotation.required(), annotation.defaultValue());
} }
} }
} }

View File

@ -30,10 +30,18 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with * Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not
* {@link RequestParam @RequestParam}. * specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
* method arguments with a request parameter name.
*
* <p>The created {@link Map} contains all request parameter name/value pairs. If the method parameter type
* is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for
* cases where request parameters have multiple values.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
* @see RequestParamMethodArgumentResolver
*/ */
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver { public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@ -47,10 +55,6 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
return false; return false;
} }
public boolean usesResponseArgument(MethodParameter parameter) {
return false;
}
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,

View File

@ -16,6 +16,7 @@
package org.springframework.web.method.annotation.support; package org.springframework.web.method.annotation.support;
import java.beans.PropertyEditor;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -24,38 +25,51 @@ import javax.servlet.ServletException;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.multipart.MultipartRequest;
/** /**
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with * Resolves method arguments annotated with @{@link RequestParam}.
* {@link RequestParam @RequestParam}. *
* <p>If the method parameter type is {@link Map}, the request parameter name is resolved and then converted
* to a {@link Map} via type conversion assuming a suitable {@link PropertyEditor} or {@link Converter} is
* registered. Alternatively, see {@link RequestParamMapMethodArgumentResolver} for access to all request
* parameters in a {@link Map}.
*
* <p>If this class is created with default resolution mode on, simple types not annotated
* with @{@link RequestParam} are also treated as request parameters with the parameter name based
* on the method argument name. See the class constructor for more details.
*
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved request header values that
* don't yet match the method parameter type.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1
* @see RequestParamMapMethodArgumentResolver
*/ */
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
private final boolean resolveParamsWithoutAnnotations; private final boolean useDefaultResolution;
/** /**
* Creates a {@link RequestParamMethodArgumentResolver} instance. * @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* * in default values, or {@code null} if default values are not expected to contain expressions
* @param beanFactory the bean factory to use for resolving default value expressions * @param useDefaultResolution in default resolution mode a method argument that is a simple type, as
* @param resolveParamsWithoutAnnotations enable default resolution mode in which parameters without * defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a request parameter even if it doesn't have
* annotations that are simple types (see {@link BeanUtils#isSimpleProperty(Class)}) * an @{@link RequestParam} annotation, the request parameter name is derived from the method parameter name.
* are also treated as model attributes with a default name based on the method argument name.
*/ */
public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory, public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
boolean resolveParamsWithoutAnnotations) { boolean useDefaultResolution) {
super(beanFactory); super(beanFactory);
this.resolveParamsWithoutAnnotations = resolveParamsWithoutAnnotations; this.useDefaultResolution = useDefaultResolution;
} }
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
@ -67,7 +81,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
} }
return true; return true;
} }
else if (this.resolveParamsWithoutAnnotations && !parameter.hasParameterAnnotations()) { else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(paramType); return BeanUtils.isSimpleProperty(paramType);
} }
else { else {
@ -84,18 +98,16 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
} }
@Override @Override
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
MethodParameter parameter,
String paramName) throws Exception {
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class); MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) { if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(paramName); List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) { if (!files.isEmpty()) {
return (files.size() == 1 ? files.get(0) : files); return (files.size() == 1 ? files.get(0) : files);
} }
} }
String[] paramValues = webRequest.getParameterValues(paramName); String[] paramValues = webRequest.getParameterValues(name);
if (paramValues != null) { if (paramValues != null) {
return paramValues.length == 1 ? paramValues[0] : paramValues; return paramValues.length == 1 ? paramValues[0] : paramValues;
} }
@ -119,5 +131,4 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
super(annotation.value(), annotation.required(), annotation.defaultValue()); super(annotation.value(), annotation.required(), annotation.defaultValue());
} }
} }
} }

View File

@ -17,7 +17,6 @@
package org.springframework.web.method.annotation.support; package org.springframework.web.method.annotation.support;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -69,11 +68,6 @@ public class ModelMethodProcessorTests {
this.webRequest = new ServletWebRequest(new MockHttpServletRequest()); this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
} }
@Test
public void usesResponseArgument() {
assertFalse(resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue(resolver.supportsParameter(modelParameter)); assertTrue(resolver.supportsParameter(modelParameter));

View File

@ -35,7 +35,6 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.RequestHeaderMapMethodArgumentResolver;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -72,11 +71,6 @@ public class RequestHeaderMapMethodArgumentResolverTests {
} }
@Test
public void usesResponseArgument() throws NoSuchMethodException {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter)); assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter));

View File

@ -35,7 +35,6 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.method.annotation.support.RequestHeaderMethodArgumentResolver;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -85,11 +84,6 @@ public class RequestHeaderMethodArgumentResolverTests {
RequestContextHolder.resetRequestAttributes(); RequestContextHolder.resetRequestAttributes();
} }
@Test
public void usesResponseArgument() throws NoSuchMethodException {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter)); assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter));

View File

@ -34,7 +34,6 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.RequestParamMapMethodArgumentResolver;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -68,11 +67,6 @@ public class RequestParamMapMethodArgumentResolverTests {
} }
@Test
public void usesResponseArgument() throws NoSuchMethodException {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter)); assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter));

View File

@ -36,7 +36,6 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
/** /**
@ -82,11 +81,6 @@ public class RequestParamMethodArgumentResolverTests {
} }
@Test
public void usesResponseArgument() throws NoSuchMethodException {
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
}
@Test @Test
public void supportsParameter() { public void supportsParameter() {
assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter)); assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter));
@ -154,8 +148,6 @@ public class RequestParamMethodArgumentResolverTests {
@RequestParam(value = "file") MultipartFile file, @RequestParam(value = "file") MultipartFile file,
@RequestParam Map<?, ?> unsupported, @RequestParam Map<?, ?> unsupported,
String plainParam) { String plainParam) {
} }
} }

View File

@ -33,7 +33,6 @@ import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -42,7 +41,7 @@ public class WebArgumentResolverAdapterTests {
private WebArgumentResolver adaptee; private WebArgumentResolver adaptee;
private WebArgumentResolverAdapter adapter; private TestWebArgumentResolverAdapter adapter;
private MethodParameter parameter; private MethodParameter parameter;
@ -51,7 +50,7 @@ public class WebArgumentResolverAdapterTests {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
adaptee = createMock(WebArgumentResolver.class); adaptee = createMock(WebArgumentResolver.class);
adapter = new WebArgumentResolverAdapter(adaptee); adapter = new TestWebArgumentResolverAdapter(adaptee);
parameter = new MethodParameter(getClass().getMethod("handle", Integer.TYPE), 0); parameter = new MethodParameter(getClass().getMethod("handle", Integer.TYPE), 0);
@ -162,4 +161,17 @@ public class WebArgumentResolverAdapterTests {
public void handle(int param) { public void handle(int param) {
} }
private class TestWebArgumentResolverAdapter extends AbstractWebArgumentResolverAdapter {
public TestWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
super(adaptee);
}
@Override
protected NativeWebRequest getWebRequest() {
return WebArgumentResolverAdapterTests.this.webRequest;
}
}
} }