提交 6fe0ff9e 编写于 作者: R Rossen Stoyanchev

ModelAndViewContainer related refinements

上级 3d8b476f
#Mon Jul 27 18:14:39 EDT 2009
#Thu Apr 07 14:34:40 BST 2011
eclipse.preferences.version=1
formatter_profile=_Spring
formatter_settings_version=11
......@@ -41,7 +41,6 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.ui.ModelMap;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
......@@ -67,13 +66,14 @@ import org.springframework.web.method.annotation.support.RequestHeaderMapMethodA
import org.springframework.web.method.annotation.support.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.support.RequestParamMapMethodArgumentResolver;
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
......@@ -459,13 +459,25 @@ public class RequestMappingHandlerMethodAdapter extends AbstractHandlerMethodAda
ServletWebRequest webRequest = new ServletWebRequest(request, response);
SessionStatus sessionStatus = new SimpleSessionStatus();
ModelMap implicitModel = modelFactory.createModel(webRequest, requestMethod);
ModelAndView mav = requestMethod.invokeAndHandle(webRequest, implicitModel, sessionStatus);
ModelMap actualModel = (mav != null) ? mav.getModelMap() : null;
modelFactory.updateAttributes(webRequest, sessionStatus, actualModel, implicitModel);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, requestMethod);
requestMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus);
return mav;
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
if (!mavContainer.isResolveView()) {
return null;
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
}
private WebDataBinderFactory createDataBinderFactory(HandlerMethod handlerMethod) {
......
......@@ -32,8 +32,6 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.support.WebArgumentResolver;
......@@ -47,9 +45,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
......@@ -60,17 +59,15 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebA
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
/**
* An extension of {@link AbstractHandlerMethodExceptionResolver} that matches thrown exceptions to
* {@link ExceptionHandler @ExceptionHandler} methods in the handler. If a match is found the
* exception-handling method is invoked to process the request.
* A {@link AbstractHandlerMethodExceptionResolver} that matches thrown exceptions to {@link ExceptionHandler}-annotated
* methods. If a match is found the exception-handling method is invoked to process the request.
*
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values
* for exception-handling methods. You can customize method argument resolution and return value
* processing through the various bean properties in this class.
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values for exception-handling
* methods. You can customize method argument resolution and return value processing through the various bean properties
* in this class.
*
* @author Rossen Stoyanchev
* @since 3.1
* @see #setMessageConverters(HttpMessageConverter[])
*/
public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandlerMethodExceptionResolver implements
InitializingBean {
......@@ -79,8 +76,6 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
private HttpMessageConverter<?>[] messageConverters;
private ModelAndViewResolver[] customModelAndViewResolvers;
private final Map<Class<?>, ExceptionMethodMapping> exceptionMethodMappingCache =
new ConcurrentHashMap<Class<?>, ExceptionMethodMapping>();
......@@ -102,9 +97,11 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
}
/**
* Set a custom ArgumentResolvers to use for special method parameter types.
* Set a custom ArgumentResolver to use for special method parameter types.
* <p>Such a custom ArgumentResolver will kick in first, having a chance to resolve
* an argument value before the standard argument handling kicks in.
* <p>Note: this is provided for backward compatibility. The preferred way to do this is to
* implement a {@link HandlerMethodArgumentResolver}.
*/
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
......@@ -114,6 +111,8 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
* Set one or more custom ArgumentResolvers to use for special method parameter types.
* <p>Any such custom ArgumentResolver will kick in first, having a chance to resolve
* an argument value before the standard argument handling kicks in.
* <p>Note: this is provided for backward compatibility. The preferred way to do this is to
* implement a {@link HandlerMethodArgumentResolver}.
*/
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
this.customArgumentResolvers = argumentResolvers;
......@@ -127,24 +126,6 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
this.messageConverters = messageConverters;
}
/**
* Set a custom ModelAndViewResolvers to use for special method return types.
* <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
* a return value before the standard ModelAndView handling kicks in.
*/
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
}
/**
* Set one or more custom ModelAndViewResolvers to use for special method return types.
* <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
* a return value before the standard ModelAndView handling kicks in.
*/
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
this.customModelAndViewResolvers = customModelAndViewResolvers;
}
/**
* Set the {@link HandlerMethodArgumentResolver}s to use to resolve argument values for
* {@link ExceptionHandler} methods. This is an optional property.
......@@ -207,9 +188,14 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
returnValueHandlers.registerReturnValueHandler(new HttpEntityMethodProcessor(messageConverters));
// Default handler
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(customModelAndViewResolvers));
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(null));
}
/**
* Attempts to find an {@link ExceptionHandler}-annotated method that can handle the thrown exception.
* The exception-handling method, if found, is invoked resulting in a {@link ModelAndView}.
* @return a {@link ModelAndView} if a matching exception-handling method was found, or {@code null} otherwise
*/
@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response,
......@@ -226,13 +212,25 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
exceptionHandler.setHandlerMethodReturnValueHandlers(returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelMap model = new ExtendedModelMap();
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking exception-handling method: " + exceptionHandler);
}
ModelAndView mav = exceptionHandler.invokeAndHandle(webRequest , model , ex);
return (mav != null) ? mav : new ModelAndView();
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex);
if (!mavContainer.isResolveView()) {
return new ModelAndView();
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
}
catch (Exception invocationEx) {
logger.error("Invoking exception-handling method resulted in exception : " +
......@@ -244,6 +242,9 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
return null;
}
/**
* @return an {@link ExceptionMethodMapping} for the the given handler method, never {@code null}
*/
private ExceptionMethodMapping getExceptionMethodMapping(HandlerMethod handlerMethod) {
Class<?> handlerType = handlerMethod.getBeanType();
ExceptionMethodMapping mapping = exceptionMethodMappingCache.get(handlerType);
......@@ -256,7 +257,7 @@ public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandle
}
/**
* Pre-built MethodFilter that matches {@link ExceptionHandler @ExceptionHandler} methods.
* MethodFilter that matches {@link ExceptionHandler @ExceptionHandler} methods.
*/
public static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
......
......@@ -43,7 +43,7 @@ public class ServletInitBinderMethodDataBinderFactory extends InitBinderMethodDa
}
/**
* {@inheritDoc} creates a Servlet data binder.
* Creates a Servlet data binder.
*/
@Override
protected WebDataBinder createBinderInstance(Object target, String objectName) {
......
......@@ -20,23 +20,31 @@ import java.io.IOException;
import java.lang.reflect.Method;
import org.springframework.http.HttpStatus;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
/**
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value of the invocation
* resulting in a {@link ModelAndView} according to the {@link HandlerAdapter} contract.
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value through registered
* {@link HandlerMethodArgumentResolver}s. If the handler method is annotated with {@link ResponseStatus},
* the status on the response is set accordingly after method invocation but before return value handling.
*
* <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>
* <li>One of the {@link HandlerMethodArgumentResolver}s 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.
* <li>The request qualifies as being not modified according to {@link ServletWebRequest#isNotModified()}.
* This is used in conjunction with a "Last-Modified" header or ETag.
* <li>The status on the response was set as a result of a {@link ResponseStatus} annotation
* </ul>
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -70,36 +78,47 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
}
/**
* Invokes the method via {@link #invokeForRequest(NativeWebRequest, ModelMap, Object...)} and also handles the
* return value by invoking one of the {@link HandlerMethodReturnValueHandler} instances registered via
* {@link #setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite)}.
* If the method is annotated with {@link SessionStatus} the response status will be set.
* Invokes the method and handles the return value through registered {@link HandlerMethodReturnValueHandler}s.
* If the handler method is annotated with {@link ResponseStatus}, the status on the response is set accordingly
* after method invocation but before return value handling.
* <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>
* <li>One of the {@link HandlerMethodArgumentResolver}s 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.
* <li>The request qualifies as being not modified according to {@link ServletWebRequest#isNotModified()}.
* This is used in conjunction with a "Last-Modified" header or ETag.
* <li>The status on the response was set as a result of a {@link ResponseStatus} annotation
* </ul>
* <p>After the call, use the {@link ModelAndViewContainer} parameter to access model attributes and view selection
* and to determine if view resolution is needed.
*
* @param request the current request
* @param model the model used throughout the current request
* @param providedArgs argument values to use as-is if they match to a method parameter's type
* @return ModelAndView object with the name of the view and the required model data, or <code>null</code>
* if the response was handled
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param providedArgs argument values to try to use without the need for view resolution
*/
public final ModelAndView invokeAndHandle(NativeWebRequest request,
ModelMap model,
Object... providedArgs) throws Exception {
public final void invokeAndHandle(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object...providedArgs) throws Exception {
if (!returnValueHandlers.supportsReturnType(getReturnType())) {
throw new IllegalStateException("No suitable HandlerMethodReturnValueHandler for method " + toString());
}
Object returnValue = invokeForRequest(request, model, providedArgs);
Object returnValue = invokeForRequest(request, mavContainer, providedArgs);
setResponseStatus((ServletWebRequest) request);
if (returnValue == null && (isRequestNotModified(request) || usesResponseArgument())) {
return null;
if (returnValue == null) {
if (isRequestNotModified(request) || hasResponseStatus() || !mavContainer.isResolveView()) {
mavContainer.setResolveView(false);
return;
}
}
ModelAndViewContainer mavContainer = new ModelAndViewContainer(model);
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
mavContainer.setResolveView(true);
return getModelAndView(request, mavContainer, returnValue);
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
}
/**
......@@ -120,38 +139,17 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
}
/**
* Create a {@link ModelAndView} from a {@link ModelAndViewContainer}.
* Does the request qualify as not modified?
*/
private ModelAndView getModelAndView(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object returnValue) {
if (returnValueHandlerUsesResponseArgument()) {
return null;
}
else {
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
mav.setViewName(mavContainer.getViewName());
if (mavContainer.getView() != null) {
mav.setView((View) mavContainer.getView());
}
return mav;
}
private boolean isRequestNotModified(NativeWebRequest request) {
return ((ServletWebRequest) request).isNotModified();
}
/**
* Check whether the request qualifies as not modified...
* TODO: document fully including sample user code
* Does the method set the response status?
*/
private boolean isRequestNotModified(NativeWebRequest request) {
ServletWebRequest servletRequest = (ServletWebRequest) request;
return (servletRequest.isNotModified() || (responseStatus != null) || usesResponseArgument());
}
protected boolean usesResponseArgument() {
return (super.usesResponseArgument() || returnValueHandlerUsesResponseArgument() || (responseStatus != null));
private boolean hasResponseStatus() {
return responseStatus != null;
}
private boolean returnValueHandlerUsesResponseArgument() {
return returnValueHandlers.usesResponseArgument(getReturnType());
}
}
}
\ No newline at end of file
......@@ -68,7 +68,7 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
if (mav != ModelAndViewResolver.UNRESOLVED) {
mavContainer.setView(mav.getView());
mavContainer.setViewName(mav.getViewName());
mavContainer.addModelAttributes(mav.getModel());
mavContainer.addAllAttributes(mav.getModel());
return;
}
}
......@@ -78,7 +78,7 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
mavContainer.addModelAttribute(name, returnValue);
mavContainer.addAttribute(name, returnValue);
}
// should not happen
......
......@@ -35,7 +35,6 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.support.WebDataBinderFactory;
......@@ -74,7 +73,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
......@@ -116,19 +115,25 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
mavContainer.setResolveView(false);
if (returnValue == null) {
return;
}
HttpOutputMessage outputMessage = createOutputMessage(webRequest);
Assert.isInstanceOf(HttpEntity.class, returnValue);
HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
HttpOutputMessage outputMessage = createOutputMessage(webRequest);
if (responseEntity instanceof ResponseEntity) {
((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
}
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
outputMessage.getHeaders().putAll(entityHeaders);
}
Object body = responseEntity.getBody();
if (body != null) {
writeWithMessageConverters(body, createInputMessage(webRequest), outputMessage);
......
......@@ -46,10 +46,10 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
ModelAndView mav = (ModelAndView) returnValue;
mavContainer.setView(mav.getView());
mavContainer.setViewName(mav.getViewName());
mavContainer.addModelAttributes(mav.getModel());
mavContainer.addAllAttributes(mav.getModel());
}
else {
// TODO
mavContainer.setResolveView(false);
}
}
......
......@@ -27,7 +27,6 @@ import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.ui.ModelMap;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -66,7 +65,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
......@@ -82,8 +81,8 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException {
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setResolveView(false);
if (returnValue != null) {
writeWithMessageConverters(webRequest, returnValue);
}
......
......@@ -27,10 +27,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils;
......@@ -55,7 +55,7 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
......
......@@ -24,10 +24,10 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletResponse} and related arguments.
......@@ -47,12 +47,14 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException {
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> parameterType = parameter.getParameterType();
mavContainer.setResolveView(false);
if (ServletResponse.class.isAssignableFrom(parameterType)) {
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
......@@ -67,7 +69,9 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
else if (Writer.class.isAssignableFrom(parameterType)) {
return response.getWriter();
}
// should not happen
throw new UnsupportedOperationException();
else {
// should not happen
throw new UnsupportedOperationException();
}
}
}
......@@ -35,8 +35,6 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.bind.annotation.ModelAttribute;
......@@ -49,8 +47,7 @@ import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
/**
......@@ -106,10 +103,10 @@ public class ControllerMethodAnnotationDetectionTests {
NativeWebRequest webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
servletRequest.setParameter("name", "Chad");
ModelMap model = new ExtendedModelMap();
ModelAndView mav = requestMappingMethod.invokeAndHandle(webRequest, model);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
Object modelAttr = mav.getModelMap().get("attrName");
Object modelAttr = mavContainer.getAttribute("attrName");
assertEquals(TestBean.class, modelAttr.getClass());
assertEquals("Chad", ((TestBean) modelAttr).getName());
......
......@@ -28,6 +28,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
/**
......@@ -56,7 +57,7 @@ public class ServletInvocableHandlerMethodTests {
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Handler(), method);
handlerMethod.setHandlerMethodReturnValueHandlers(handlers);
handlerMethod.invokeAndHandle(webRequest, null);
handlerMethod.invokeAndHandle(webRequest, new ModelAndViewContainer());
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
assertEquals("400 Bad Request", response.getErrorMessage());
......
......@@ -46,6 +46,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
/**
......@@ -65,16 +66,18 @@ public class HttpEntityMethodProcessorTests {
private MethodParameter intReturnValue;
private MethodParameter httpEntityReturnValue;
private MethodParameter intParameter;
private ModelAndViewContainer mavContainer;
private ServletWebRequest request;
private MockHttpServletRequest servletRequest;
private MockHttpServletResponse servletResponse;
private MethodParameter httpEntityReturnValue;
private MethodParameter intParameter;
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
......@@ -93,6 +96,8 @@ public class HttpEntityMethodProcessorTests {
Method other = getClass().getMethod("otherMethod");
intReturnValue = new MethodParameter(other, -1);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
servletResponse = new MockHttpServletResponse();
request = new ServletWebRequest(servletRequest, servletResponse);
......@@ -135,7 +140,7 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, null, request, null);
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, mavContainer, request, null);
assertEquals("Invalid argument", expected, result.getBody());
verify(messageConverter);
......@@ -152,14 +157,14 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.resolveArgument(httpEntityParam, null, request, null);
processor.resolveArgument(httpEntityParam, mavContainer, request, null);
verify(messageConverter);
}
@Test(expected = HttpMediaTypeNotSupportedException.class)
public void resolveArgumentNoContentType() throws Exception {
processor.resolveArgument(httpEntityParam, null, request, null);
processor.resolveArgument(httpEntityParam, mavContainer, request, null);
}
@Test
......@@ -175,8 +180,9 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
......@@ -193,8 +199,9 @@ public class HttpEntityMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
......@@ -205,8 +212,9 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity<String> returnValue = new ResponseEntity<String>(responseHeaders, HttpStatus.ACCEPTED);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
assertEquals("headerValue", servletResponse.getHeader("header"));
}
......@@ -217,8 +225,9 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
processor.handleReturnValue(returnValue, responseEntityReturnValue, mavContainer, request);
assertFalse(mavContainer.isResolveView());
assertEquals("headerValue", servletResponse.getHeader("header"));
}
......
......@@ -44,6 +44,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
/**
......@@ -63,6 +64,8 @@ public class RequestResponseBodyMethodProcessorTests {
private MethodParameter intReturnValue;
private ModelAndViewContainer mavContainer;
private NativeWebRequest webRequest;
private MockHttpServletRequest servletRequest;
......@@ -79,6 +82,8 @@ public class RequestResponseBodyMethodProcessorTests {
Method other = getClass().getMethod("otherMethod");
intReturnValue = new MethodParameter(other, -1);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
webRequest = new ServletWebRequest(servletRequest, servletResponse);
......@@ -116,7 +121,7 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
Object result = processor.resolveArgument(stringParameter, null, webRequest, null);
Object result = processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
assertEquals("Invalid argument", expected, result);
verify(messageConverter);
......@@ -134,14 +139,14 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.resolveArgument(stringParameter, null, webRequest, null);
processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
verify(messageConverter);
}
@Test(expected = HttpMediaTypeNotSupportedException.class)
public void resolveArgumentNoContentType() throws Exception {
processor.resolveArgument(stringParameter, null, webRequest, null);
processor.resolveArgument(stringParameter, mavContainer, webRequest, null);
}
@Test
......@@ -156,8 +161,9 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
......@@ -173,8 +179,9 @@ public class RequestResponseBodyMethodProcessorTests {
replay(messageConverter);
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
processor.handleReturnValue(returnValue, stringReturnValue, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView());
verify(messageConverter);
}
......
......@@ -28,6 +28,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
import static org.junit.Assert.*;
......@@ -45,12 +46,14 @@ public class ServletResponseMethodArgumentResolverTests {
private MockHttpServletResponse servletResponse;
private ModelAndViewContainer mavContainer;
@Before
public void setUp() throws Exception {
resolver = new ServletResponseMethodArgumentResolver();
supportedParams =
getClass().getMethod("supportedParams", ServletResponse.class, OutputStream.class, Writer.class);
supportedParams = getClass().getMethod("supportedParams", ServletResponse.class, OutputStream.class, Writer.class);
servletResponse = new MockHttpServletResponse();
mavContainer = new ModelAndViewContainer();
webRequest = new ServletWebRequest(new MockHttpServletRequest(), servletResponse);
}
......@@ -65,8 +68,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("ServletResponse not supported", resolver.supportsParameter(servletResponseParameter));
Object result = resolver.resolveArgument(servletResponseParameter, null, webRequest, null);
Object result = resolver.resolveArgument(servletResponseParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse, result);
assertFalse(mavContainer.isResolveView());
}
@Test
......@@ -75,8 +79,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("OutputStream not supported", resolver.supportsParameter(outputStreamParameter));
Object result = resolver.resolveArgument(outputStreamParameter, null, webRequest, null);
Object result = resolver.resolveArgument(outputStreamParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getOutputStream(), result);
assertFalse(mavContainer.isResolveView());
}
@Test
......@@ -85,8 +90,9 @@ public class ServletResponseMethodArgumentResolverTests {
assertTrue("Writer not supported", resolver.supportsParameter(writerParameter));
Object result = resolver.resolveArgument(writerParameter, null, webRequest, null);
Object result = resolver.resolveArgument(writerParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getWriter(), result);
assertFalse(mavContainer.isResolveView());
}
public void supportedParams(ServletResponse p0, OutputStream p1, Writer p2) {
......
#Thu Dec 18 06:36:28 PST 2008
#Thu Apr 07 14:34:33 BST 2011
eclipse.preferences.version=1
formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
formatter_settings_version=11
......@@ -44,7 +44,7 @@ import org.springframework.util.ClassUtils;
public class HandlerMethod {
/** Logger that is available to subclasses */
protected final Log logger = LogFactory.getLog(this.getClass());
protected final Log logger = LogFactory.getLog(getClass());
private final Object bean;
......
......@@ -29,7 +29,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* Defines the algorithm for searching handler methods exhaustively including interfaces and parent
* classes while also dealing with parameterized methods and interface and class-based proxies.
* classes while also dealing with parameterized methods as well as interface and class-based proxies.
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -42,6 +42,7 @@ public abstract class HandlerMethodSelector {
*
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
*/
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
......
......@@ -29,8 +29,8 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.InvocableHandlerMethod;
/**
* A specialization of {@link DefaultDataBinderFactory} that further initializes {@link WebDataBinder} instances
* through the invocation one or more {@link InitBinder} methods.
* A specialization of {@link DefaultDataBinderFactory} that further initializes {@link WebDataBinder} instances
* by invoking {@link InitBinder} methods.
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -41,7 +41,7 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
/**
* Create an {@code InitBinderMethodDataBinderFactory} instance with the given {@link InitBinder} methods.
* @param initBinderMethods {@link InitBinder} methods to use when initializing new data binder instances
* @param initBinderMethods {@link InitBinder} methods to use to invoke to initialize new data binder instances
* @param bindingInitializer a {@link WebBindingInitializer} to initialize new data binder instances with
*/
public InitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> initBinderMethods,
......@@ -51,9 +51,9 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
}
/**
* Create a new {@link WebDataBinder} for the given target object and initialize it through the invocation
* of {@link InitBinder} methods. Only {@link InitBinder} annotations that don't specify attributes names
* and {@link InitBinder} annotations that contain the target object name are invoked.
* Create a {@link WebDataBinder} for the given object and initialize it by calling {@link InitBinder} methods.
* Only methods with an {@link InitBinder} annotation value that doesn't list attributes names or methods with
* an {@link InitBinder} annotation value that matches the target object name are invoked.
* @see InitBinder#value()
*/
@Override
......@@ -76,4 +76,4 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
return dataBinder;
}
}
}
\ No newline at end of file
......@@ -26,11 +26,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.core.Conventions;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.support.BindingAwareModelMap;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
......@@ -40,17 +38,17 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Provides methods to create and update the "implicit" model for a given request.
* Provides methods to create and update a model in the context of a given request.
*
* <p>{@link #createModel(NativeWebRequest, HandlerMethod)} prepares a model for use with
* a {@link RequestMapping} method. The model is populated with handler session attributes as well
* as request attributes obtained by invoking model attribute methods.
* <p>{@link #initModel(NativeWebRequest, ModelAndViewContainer, HandlerMethod)} populates the model
* with handler session attributes and attributes from model attribute methods.
*
* <p>{@link #updateAttributes(NativeWebRequest, SessionStatus, ModelMap, ModelMap)} updates
* the model used for the invocation of a {@link RequestMapping} method, adding handler session
* attributes and {@link BindingResult} structures as necessary.
* <p>{@link #updateModel(NativeWebRequest, ModelAndViewContainer, SessionStatus)} updates
* the model (usually after the {@link RequestMapping} method has been called) promoting attributes
* to the session and adding {@link BindingResult} attributes as necessary.
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -66,8 +64,8 @@ public final class ModelFactory {
/**
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
* @param attributeMethods {@link ModelAttribute}-annotated methods to invoke when populating a model
* @param binderFactory the binder factory to use to add {@link BindingResult} instances to the model
* @param sessionHandler a session attributes handler to synch attributes in the model with the session
* @param binderFactory the binder factory to use when adding {@link BindingResult}s to the model
* @param sessionHandler a session attributes handler to synch attributes with the session
*/
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
WebDataBinderFactory binderFactory,
......@@ -78,58 +76,78 @@ public final class ModelFactory {
}
/**
* Prepare a model for the current request obtaining attributes in the following order:
* Populate the model for a request with attributes obtained in the following order:
* <ol>
* <li>Retrieve previously accessed handler session attributes from the session
* <li>Add known (i.e. previously accessed) handler session attributes
* <li>Invoke model attribute methods
* <li>Find request-handling method {@link ModelAttribute}-annotated arguments that are handler session attributes
* <li>Check if any {@link ModelAttribute}-annotated arguments need to be added from the session
* </ol>
* <p>As a general rule a model attribute is added only once following the above order.
* <p>As a general rule model attributes are added only once.
*
* @param request the current request
* @param mavContainer the {@link ModelAndViewContainer} to add model attributes to
* @param requestMethod the request handling method for which the model is needed
* @return the created model
* @throws Exception if an exception occurs while invoking model attribute methods
*/
public ModelMap createModel(NativeWebRequest request, HandlerMethod requestMethod) throws Exception {
ExtendedModelMap model = new BindingAwareModelMap();
Map<String, ?> sessionAttributes = this.sessionHandler.retrieveHandlerSessionAttributes(request);
model.addAllAttributes(sessionAttributes);
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod requestMethod)
throws Exception {
invokeAttributeMethods(request, model);
Map<String, ?> sessionAttributes = this.sessionHandler.retrieveHandlerSessionAttributes(request);
mavContainer.addAllAttributes(sessionAttributes);
addSessionAttributesByName(request, requestMethod, model);
invokeAttributeMethods(request, mavContainer);
return model;
addSessionAttributesByName(request, mavContainer, requestMethod);
}
/**
* Populate the model by invoking model attribute methods. If two methods provide the same attribute,
* the attribute produced by the first method is used.
* Invoke model attribute methods to populate the model.
* If two methods return the same attribute, the attribute from the first method is added.
*/
private void invokeAttributeMethods(NativeWebRequest request, ExtendedModelMap model) throws Exception {
private void invokeAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
for (InvocableHandlerMethod attrMethod : this.attributeMethods) {
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
if (StringUtils.hasText(modelName) && model.containsAttribute(modelName)) {
if (mavContainer.containsAttribute(modelName)) {
continue;
}
Object returnValue = attrMethod.invokeForRequest(request, model);
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
if (!attrMethod.isVoid()){
String valueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!model.containsAttribute(valueName)) {
model.addAttribute(valueName, returnValue);
mavContainer.mergeAttribute(valueName, returnValue);
}
}
}
/**
* Check if {@link ModelAttribute}-annotated arguments are handler session attributes and add them from the session.
*/
private void addSessionAttributesByName(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod requestMethod) {
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
continue;
}
String attrName = getNameForParameter(parameter);
if (!mavContainer.containsAttribute(attrName)) {
if (sessionHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
Object attrValue = sessionHandler.retrieveAttribute(request, attrName);
if (attrValue == null){
new HttpSessionRequiredException("Session attribute '" + attrName + "' not found in session");
}
mavContainer.addAttribute(attrName, attrValue);
}
}
}
}
/**
* Derive the model name for the given method return value using one of the following:
* Derive the model attribute name for the given return value using one of the following:
* <ol>
* <li>The method {@link ModelAttribute} annotation value
* <li>The name of the return type
* <li>The name of the return type
* <li>The name of the return value type if the method return type is {@code Object}
* </ol>
* @param returnValue the value returned from a method invocation
......@@ -149,29 +167,7 @@ public final class ModelFactory {
}
/**
* Find request-handling method, {@link ModelAttribute}-annotated arguments that are handler session attributes
* and add them to the model if not present.
*/
private void addSessionAttributesByName(NativeWebRequest request, HandlerMethod requestMethod, ModelMap model) {
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
if (!parameter.hasParameterAnnotation(ModelAttribute.class)) {
continue;
}
String attrName = getNameForParameter(parameter);
if (!model.containsKey(attrName)) {
if (sessionHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
Object attrValue = sessionHandler.retrieveAttribute(request, attrName);
if (attrValue == null){
new HttpSessionRequiredException("Session attribute '" + attrName + "' not found in session");
}
model.addAttribute(attrName, attrValue);
}
}
}
}
/**
* Derives the model name for the given method parameter using one of the following:
* Derives the model attribute name for the given method parameter using one of the following:
* <ol>
* <li>The parameter {@link ModelAttribute} annotation value
* <li>The name of the parameter type
......@@ -185,33 +181,29 @@ public final class ModelFactory {
}
/**
* Clean up handler session attributes when {@link SessionStatus#isComplete()} is {@code true}.
* Promote model attributes to the session. Add {@link BindingResult} attributes where missing.
* Updates the model by cleaning handler session attributes depending on {@link SessionStatus#isComplete()},
* promotes model attributes to the session, and adds {@link BindingResult} attributes where missing.
* @param request the current request
* @param sessionStatus indicates whether handler session attributes are to be cleaned
* @param actualModel the model returned from the request method, or {@code null} when the response was handled
* @param implicitModel the model for the request
* @throws Exception if the process of creating {@link BindingResult} attributes causes a problem
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param sessionStatus whether session processing is complete
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
*/
public void updateAttributes(NativeWebRequest request,
SessionStatus sessionStatus,
ModelMap actualModel,
ModelMap implicitModel) throws Exception {
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
throws Exception {
if (sessionStatus.isComplete()){
this.sessionHandler.cleanupHandlerSessionAttributes(request);
}
if (actualModel != null) {
this.sessionHandler.storeHandlerSessionAttributes(request, actualModel);
updateBindingResult(request, actualModel);
this.sessionHandler.storeHandlerSessionAttributes(request, mavContainer.getModel());
if (mavContainer.isResolveView()) {
updateBindingResult(request, mavContainer.getModel());
}
else {
this.sessionHandler.storeHandlerSessionAttributes(request, implicitModel);
}
}
/**
* Add {@link BindingResult} structures to the model for attributes that require it.
* Add {@link BindingResult} attributes to the model for attributes that require it.
*/
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
List<String> keyNames = new ArrayList<String>(model.keySet());
......
......@@ -174,4 +174,5 @@ public class SessionAttributesHandler {
}
}
}
}
}
\ No newline at end of file
......@@ -25,13 +25,13 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Abstract base class for argument resolvers that resolve named values.
......@@ -59,7 +59,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
}
public final Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
......
......@@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* An implementation of {@link HandlerMethodArgumentResolver} that resolves {@link Errors} method parameters.
......@@ -47,9 +48,10 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
ModelMap model = mavContainer.getModel();
if (model.size() > 0) {
List<String> keys = new ArrayList<String>(model.keySet());
String lastKey = keys.get(model.size()-1);
......
......@@ -20,13 +20,11 @@ import java.lang.annotation.Annotation;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.support.SessionAttributeStore;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.bind.support.WebRequestDataBinder;
import org.springframework.web.context.request.NativeWebRequest;
......@@ -77,18 +75,16 @@ public class ModelAttributeMethodProcessor
}
/**
* Creates a {@link WebDataBinder} for the target model attribute and applies data binding to it.
* The model attribute may be obtained from the "implicit" model, from the session via, or by
* direct instantiation.
*
* @throws Exception if invoking an data binder initialization fails or if data binding and/or
* validation results in errors and the next method parameter is not of type {@link Errors}.
* Resolves the argument to a model attribute creating a {@link WebDataBinder} and invoking data binding on it.
* The model attribute is obtained from the model first or otherwise created via direct instantiation.
* @throws Exception if data binder initialization fails or if data binding results in errors and the next
* method parameter is not of type {@link Errors}.
*/
public final Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
WebDataBinder binder = createDataBinder(parameter, model, webRequest, binderFactory);
WebDataBinder binder = createDataBinder(parameter, mavContainer, webRequest, binderFactory);
if (binder.getTarget() != null) {
doBind(binder, webRequest);
......@@ -102,24 +98,23 @@ public class ModelAttributeMethodProcessor
}
}
model.putAll(binder.getBindingResult().getModel());
mavContainer.addAllAttributes(binder.getBindingResult().getModel());
return binder.getTarget();
}
/**
* Creates a {@link WebDataBinder} for a target object which may be obtained from the "implicit" model,
* the session via {@link SessionAttributeStore}, or by direct instantiation.
* Creates a {@link WebDataBinder} for a target object.
*/
private WebDataBinder createDataBinder(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
String attrName = ModelFactory.getNameForParameter(parameter);
Object target;
if (model.containsKey(attrName)) {
target = model.get(attrName);
if (mavContainer.containsAttribute(attrName)) {
target = mavContainer.getAttribute(attrName);
}
else {
target = BeanUtils.instantiateClass(parameter.getParameterType());
......@@ -166,7 +161,7 @@ public class ModelAttributeMethodProcessor
NativeWebRequest webRequest) throws Exception {
if (returnValue != null) {
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
mavContainer.addModelAttribute(name, returnValue);
mavContainer.addAttribute(name, returnValue);
}
}
......
......@@ -20,7 +20,6 @@ import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
......@@ -46,19 +45,10 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
if (Model.class.isAssignableFrom(paramType)) {
return model;
}
else if (Map.class.isAssignableFrom(paramType)) {
return model;
}
// should not happen
throw new UnsupportedOperationException();
return mavContainer.getModel();
}
public boolean supportsReturnType(MethodParameter returnType) {
......@@ -78,10 +68,10 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
return;
}
if (returnValue instanceof Model) {
mavContainer.addModelAttributes((Model) returnValue);
mavContainer.addAllAttributes(((Model) returnValue).asMap());
}
else if (returnValue instanceof Map){
mavContainer.addModelAttributes((Map) returnValue);
mavContainer.addAllAttributes((Map) returnValue);
}
else {
// should not happen
......
......@@ -22,13 +22,13 @@ import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.ui.ModelMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
......@@ -48,8 +48,8 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
NativeWebRequest webRequest,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
......
......@@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
......@@ -28,6 +27,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
......@@ -52,7 +52,7 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
......
......@@ -19,7 +19,6 @@ package org.springframework.web.method.annotation.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.support.WebArgumentResolver;
......@@ -28,6 +27,7 @@ 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.ModelAndViewContainer;
/**
* Adapts a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver} contract.
......@@ -75,7 +75,7 @@ public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver
}
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
......
......@@ -17,21 +17,20 @@
package org.springframework.web.method.support;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
/**
* Strategy interface for resolving method parameters into argument values from a given request.
* Strategy interface for resolving method parameters into argument values in the context of a given request.
*
* @author Arjen Poutsma
* @since 3.1
*/
public interface HandlerMethodArgumentResolver extends HandlerMethodProcessor {
public interface HandlerMethodArgumentResolver {
/**
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
* Whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
*
* @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise
......@@ -39,20 +38,20 @@ public interface HandlerMethodArgumentResolver extends HandlerMethodProcessor {
boolean supportsParameter(MethodParameter parameter);
/**
* Resolves a method parameter into an argument value from a given request and a {@link ModelMap} providing
* the ability to both access and add new model attributes. A {@link WebDataBinderFactory} is also provided
* for creating a {@link WebDataBinder} instance to use for data binding and type conversion.
* Resolves a method parameter into an argument value from a given request. A {@link ModelAndViewContainer}
* provides access to the model for the request. A {@link WebDataBinderFactory} provides a way to create
* a {@link WebDataBinder} instance when needed for data binding and type conversion purposes.
*
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to
* @param parameter the method parameter to resolve. This parameter must have previously been passed to
* {@link #supportsParameter(org.springframework.core.MethodParameter)} and it must have returned {@code true}
* @param model the model for the current request
* @param webRequest the current request.
* @param binderFactory a factory in case the resolver needs to create a {@link WebDataBinder} instance
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null}.
* @throws Exception in case of errors with the preparation of argument values
*/
Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception;
......
......@@ -24,22 +24,19 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
/**
* Implementation of {@link HandlerMethodArgumentResolver} that resolves handler method arguments by delegating
* to a list of registered {@link HandlerMethodArgumentResolver}s.
*
* <p>Previously resolved method argument types are cached internally for faster lookups.
* Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s.
* Previously resolved method parameters are cached for faster lookups.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverComposite.class);
protected final Log logger = LogFactory.getLog(getClass());
private final List<HandlerMethodArgumentResolver> argumentResolvers =
new ArrayList<HandlerMethodArgumentResolver>();
......@@ -48,34 +45,34 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>();
/**
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by any of the
* registered {@link HandlerMethodArgumentResolver}s.
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
* {@link HandlerMethodArgumentResolver}.
*/
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
/**
* Resolve a method parameter into an argument value for the given request by iterating over registered
* {@link HandlerMethodArgumentResolver}s to find one that supports the given method parameter.
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
public Object resolveArgument(MethodParameter parameter,
ModelMap model,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver != null) {
return resolver.resolveArgument(parameter, model, webRequest, binderFactory);
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
else {
throw new IllegalStateException(
"No suitable HandlerMethodArgumentResolver found. " +
"supportsParameter(MethodParameter) should have been called previously.");
}
throw new IllegalStateException(
"No suitable HandlerMethodArgumentResolver found. " +
"supportsParameter(MethodParameter) should have been called previously.");
}
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
* @return a {@link HandlerMethodArgumentResolver} instance, or {@code null} if none
*/
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
......@@ -94,15 +91,6 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
}
return result;
}
/**
* Indicates whether the argument resolver that supports the given method parameter uses the response argument.
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
*/
public boolean usesResponseArgument(MethodParameter parameter) {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
return (resolver != null && resolver.usesResponseArgument(parameter));
}
/**
* Register the given {@link HandlerMethodArgumentResolver}.
......
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.method.support;
import org.springframework.core.MethodParameter;
/**
* A base interface for {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s.
*
* @author Arjen Poutsma
* @since 3.1
*/
public interface HandlerMethodProcessor {
/**
* Indicates whether the given {@linkplain org.springframework.core.MethodParameter method parameter},
* uses the response argument with the implication that the response will be handled directly by invoking
* the method and will not require view name resolution followed by rendering.
*
* @param parameter the method parameter, either a method argument or a return type
* @return {@code true} if the supplied parameter uses the response argument; {@code false} otherwise
*/
boolean usesResponseArgument(MethodParameter parameter);
}
\ No newline at end of file
......@@ -20,15 +20,15 @@ import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
/**
* Strategy interface to process the value returned from a handler method invocation.
* Strategy interface to handle the value returned from the invocation of a handler method .
*
* @author Arjen Poutsma
* @since 3.1
*/
public interface HandlerMethodReturnValueHandler extends HandlerMethodProcessor {
public interface HandlerMethodReturnValueHandler {
/**
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by this handler.
* Whether the given {@linkplain MethodParameter method return type} is supported by this handler.
*
* @param returnType the method return type to check
* @return {@code true} if this handler supports the supplied return type; {@code false} otherwise
......@@ -36,15 +36,18 @@ public interface HandlerMethodReturnValueHandler extends HandlerMethodProcessor
boolean supportsReturnType(MethodParameter returnType);
/**
* Handles the given value returned by a handler method invocation by writing directly to the response
* or by using the {@code mavContainer} argument to add model attributes and/or set the view.
* Handle the given return value by adding attributes to the model, setting the view (or view name),
* or by writing to the response. {@link HandlerMethodReturnValueHandler} implementations should also
* consider whether to set {@link ModelAndViewContainer#setResolveView(boolean)}, which is set to
* {@code true} by default and therefore needs to be set to {@code false} explicitly if view
* resolution is to be bypassed.
*
* @param returnValue the return value to handle
* @param returnType the return type to handle. This type must have previously been passed to
* @param returnValue the value returned from the handler method
* @param returnType the type of the return value. This type must have previously been passed to
* {@link #supportsReturnType(org.springframework.core.MethodParameter)} and it must have returned {@code true}
* @param mavContainer records model and view choices
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param webRequest the current request
* @throws Exception in case of errors
* @throws Exception if the return value handling results in an error
*/
void handleReturnValue(Object returnValue,
MethodParameter returnType,
......
......@@ -27,17 +27,15 @@ import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
/**
* Implementation of {@link HandlerMethodReturnValueHandler} that handles method return values by
* delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
*
* <p>Previously resolved return types are cached internally for faster lookups.
* Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
* Previously resolved return types are cached for faster lookups.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverComposite.class);
protected final Log logger = LogFactory.getLog(getClass());
private final List<HandlerMethodReturnValueHandler> returnValueHandlers =
new ArrayList<HandlerMethodReturnValueHandler>();
......@@ -46,16 +44,16 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
new ConcurrentHashMap<MethodParameter, HandlerMethodReturnValueHandler>();
/**
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by any of the
* registered {@link HandlerMethodReturnValueHandler}s.
* Whether the given {@linkplain MethodParameter method return type} is supported by any registered
* {@link HandlerMethodReturnValueHandler}.
*/
public boolean supportsReturnType(MethodParameter returnType) {
return getReturnValueHandler(returnType) != null;
}
/**
* Handles the given method return value by iterating over registered {@link HandlerMethodReturnValueHandler}s
* to find one that supports it.
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
......@@ -64,31 +62,28 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
if (handler != null) {
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
throw new IllegalStateException(
"No suitable HandlerMethodReturnValueHandler found. " +
"supportsReturnType(MethodParameter) should have been called previously");
else {
throw new IllegalStateException(
"No suitable HandlerMethodReturnValueHandler found. " +
"supportsReturnType(MethodParameter) should have been called previously");
}
}
/**
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given method return type.
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type.
*/
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
if (this.returnValueHandlers == null) {
return null;
}
HandlerMethodReturnValueHandler result = this.returnValueHandlerCache.get(returnType);
if (result == null) {
for (HandlerMethodReturnValueHandler methodReturnValueHandler : returnValueHandlers) {
for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if return value handler [" + methodReturnValueHandler + "] supports [" +
logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
returnType.getGenericParameterType() + "]");
}
if (methodReturnValueHandler.supportsReturnType(returnType)) {
result = methodReturnValueHandler;
this.returnValueHandlerCache.put(returnType, methodReturnValueHandler);
if (returnValueHandler.supportsReturnType(returnType)) {
result = returnValueHandler;
this.returnValueHandlerCache.put(returnType, returnValueHandler);
break;
}
}
......@@ -96,15 +91,6 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
return result;
}
/**
* Indicates whether the return value handler that supports the given method parameter uses the response.
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
*/
public boolean usesResponseArgument(MethodParameter parameter) {
HandlerMethodReturnValueHandler handler = getReturnValueHandler(parameter);
return (handler != null && handler.usesResponseArgument(parameter));
}
/**
* Register the given {@link HandlerMethodReturnValueHandler}.
*/
......
......@@ -24,7 +24,6 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.ui.ModelMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.SessionStatus;
......@@ -33,12 +32,15 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod;
/**
* Provides a way to invoke a handler method after resolving its method argument values through
* {@link HandlerMethodArgumentResolver}s in the context of the current request.
* Provides a method for invoking the handler method for a given request after resolving its method argument
* values through registered {@link HandlerMethodArgumentResolver}s.
*
* <p>Resolving argument values often requires a {@link WebDataBinder} for data binding and type conversion.
* Use {@link #setDataBinderFactory(WebDataBinderFactory)} to provide a factory for that. The list of argument
* resolvers can be set via {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)}.
* <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion.
* Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply a binder factory to pass to
* argument resolvers.
*
* <p>Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize
* the list of argument resolvers.
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -52,7 +54,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
/**
* Constructs a new invocable handler method with the given bean instance and method.
* Constructs a new handler method with the given bean instance and method.
* @param bean the bean instance
* @param method the method
*/
......@@ -61,7 +63,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
* Constructs a new invocable handler method with the given bean instance, method name and parameters.
* Constructs a new handler method with the given bean instance, method name and parameters.
* @param bean the object bean
* @param methodName the method name
* @param parameterTypes the method parameter types
......@@ -73,9 +75,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
* Sets the {@link WebDataBinderFactory} to be passed to argument resolvers that require a
* {@link WebDataBinder} to do type conversion or data binding on the method argument value.
*
* Sets the {@link WebDataBinderFactory} to be passed to argument resolvers allowing them to create
* a {@link WebDataBinder} for data binding and type conversion purposes.
* @param dataBinderFactory the data binder factory.
*/
public void setDataBinderFactory(WebDataBinderFactory dataBinderFactory) {
......@@ -90,30 +91,30 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
* (e.g. for default attribute names).
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
* Set the ParameterNameDiscoverer for resolving parameter names when needed (e.g. default request attribute name).
* <p>Default is an {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer} instance.
*/
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* Invoke the method after resolving its argument values based on the given request.
* <p>Most argument values are resolved with the help of {@link HandlerMethodArgumentResolver}s
* configured via {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)}.
* However, the {@code provideArgs} parameter can be used to supply argument values for use
* directly rather than relying on argument resolution - e.g. {@link WebDataBinder},
* {@link SessionStatus}, or the thrown exception in a HandlerExceptionResolver.
* Invoke the method after resolving its argument values in the context of the given request. <p>Argument
* values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
* parameter however may supply argument values to be used directly, i.e. without argument resolution.
* Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
* a thrown exception instance. Provided argument values are checked before argument resolvers.
*
* @param request the current request
* @param model the model used throughout the current request
* @param providedArgs argument values to try to use, thus bypassing argument resolution
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param providedArgs argument values to try to use without view resolution
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
*/
public final Object invokeForRequest(NativeWebRequest request, ModelMap model, Object... providedArgs)
throws Exception {
Object[] args = getMethodArguments(request, model, providedArgs);
public final Object invokeForRequest(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder builder = new StringBuilder("Invoking [");
......@@ -131,8 +132,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
return returnValue;
}
private Object[] getMethodArguments(NativeWebRequest request, ModelMap model, Object... providedArgs)
throws Exception {
/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
......@@ -141,13 +146,11 @@ public class InvocableHandlerMethod extends HandlerMethod {
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
args[i] = this.argumentResolvers.resolveArgument(parameter, model, request, dataBinderFactory);
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
}
else {
throw new IllegalStateException("Cannot resolve argument index=" + parameter.getParameterIndex() + ""
......@@ -158,6 +161,9 @@ public class InvocableHandlerMethod extends HandlerMethod {
return args;
}
/**
* Attempt to resolve a method parameter from the list of provided argument values.
*/
private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
if (providedArgs == null) {
return null;
......@@ -171,10 +177,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
* Invokes this handler method with the given argument values.
* @param args the argument values
* @return the result of the invocation
* @throws Exception when the method invocation results in an exception
* Invoke this handler method with the given argument values.
*/
private Object invoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
......@@ -224,19 +227,5 @@ public class InvocableHandlerMethod extends HandlerMethod {
throw (Exception) targetException;
}
}
/**
* Whether any of the registered {@link HandlerMethodArgumentResolver}s use the response argument.
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
*/
protected boolean usesResponseArgument() {
MethodParameter[] methodParameters = getMethodParameters();
for (MethodParameter methodParameter : methodParameters) {
if (this.argumentResolvers.usesResponseArgument(methodParameter)) {
return true;
}
}
return false;
}
}
\ No newline at end of file
......@@ -18,16 +18,21 @@ package org.springframework.web.method.support;
import java.util.Map;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.support.BindingAwareModelMap;
/**
* Contains model and view choices made by {@link HandlerMethodReturnValueHandler}s.
* Provides access to the model and a place to record model and view related decisions to all
* {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s .
*
* <p>Allows return value handlers to set only the bits that are relevant to them - i.e. model, view,
* or none, while also taking care of merging attributes added by the {@link HandlerMethodReturnValueHandler}
* with attributes from the implicit model.
* <p>In addition to storing model attributes and a view, the {@link ModelAndViewContainer} also provides
* a {@link #setResolveView(boolean)} flag, which can be used to request or bypass a view resolution phase.
* This is most commonly used from {@link HandlerMethodReturnValueHandler}s but in some cases may also be
* used from {@link HandlerMethodArgumentResolver}s such as when a handler method accepts an argument
* providing access to the response. When that is the case, if the handler method returns {@code null},
* view resolution is skipped.
*
* @author Rossen Stoyanchev
* @since 3.1
......@@ -38,44 +43,117 @@ public class ModelAndViewContainer {
private Object view;
private final ModelMap actualModel = new ExtendedModelMap();
private final ModelMap model;
private final ModelMap implicitModel;
private boolean resolveView = true;
public ModelAndViewContainer(ModelMap implicitModel) {
this.implicitModel = (implicitModel != null) ? implicitModel : new ExtendedModelMap();
/**
* Create a {@link ModelAndViewContainer} instance with a {@link BindingAwareModelMap}.
*/
public ModelAndViewContainer() {
this.model = new BindingAwareModelMap();
}
/**
* Create a {@link ModelAndViewContainer} instance with the given {@link ModelMap} instance.
* @param model the model to use
*/
public ModelAndViewContainer(ModelMap model) {
Assert.notNull(model);
this.model = model;
}
/**
* @return the model for the current request
*/
public ModelMap getModel() {
return new ExtendedModelMap().addAllAttributes(actualModel).mergeAttributes(implicitModel);
return model;
}
/**
* @return the view name to use for view resolution, or {@code null}
*/
public String getViewName() {
return this.viewName;
}
/**
* @param viewName the name of the view to use for view resolution
*/
public void setViewName(String viewName) {
this.viewName = viewName;
}
/**
* @return the view instance to use for view resolution
*/
public Object getView() {
return this.view;
}
/**
* @param view the view instance to use for view resolution
*/
public void setView(Object view) {
this.view = view;
}
/**
* @return whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
*/
public boolean isResolveView() {
return resolveView;
}
/**
* @param resolveView whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
*/
public void setResolveView(boolean resolveView) {
this.resolveView = resolveView;
}
public void addModelAttributes(Model attributes) {
actualModel.addAllAttributes(attributes.asMap());
/**
* Whether model contains an attribute of the given name.
* @param name the name of the model attribute
* @return {@code true} if the model contains an attribute by that name and the name is not an empty string
*/
public boolean containsAttribute(String name) {
return (StringUtils.hasText(name) && model.containsAttribute(name));
}
public void addModelAttributes(Map<String, Object> attributes) {
actualModel.addAllAttributes(attributes);
/**
* @param name the attribute to get from the model
* @return the attribute or {@code null}
*/
public Object getAttribute(String name) {
return model.get(name);
}
public void addModelAttribute(String name, Object value) {
actualModel.addAttribute(name, value);
/**
* Add the supplied attribute under the given name.
* @param name the name of the model attribute (never null)
* @param value the model attribute value (can be null)
*/
public void addAttribute(String name, Object value) {
model.addAttribute(name, value);
}
/**
* Copy all attributes in the supplied Map into the model
*/
public void addAllAttributes(Map<String, ?> attributes) {
model.addAllAttributes(attributes);
}
}
/**
* Add the given attribute if the model does not already contain such an attribute.
* @param name the name of the attribute to check and add
* @param value the value of the attribute
*/
public void mergeAttribute(String name, Object value) {
if (!containsAttribute(name)) {
model.addAttribute(name, value);
}
}
}
\ No newline at end of file
......@@ -32,9 +32,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
......@@ -48,6 +46,7 @@ import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Text fixture for {@link ModelFactory} unit tests.
......@@ -64,6 +63,8 @@ public class ModelFactoryTests {
private InvocableHandlerMethod requestMethod;
private ModelAndViewContainer mavContainer;
@Before
public void setUp() throws Exception {
Object handler = new ModelHandler();
......@@ -73,38 +74,39 @@ public class ModelFactoryTests {
this.sessionAttributeStore = new DefaultSessionAttributeStore();
this.handlerSessionAttributeStore = new SessionAttributesHandler(handler.getClass(), sessionAttributeStore);
this.mavContainer = new ModelAndViewContainer();
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
}
@Test
public void createModel() throws Exception {
ModelMap model = createFactory(new ModelHandler(), "model", Model.class).createModel(webRequest, requestMethod);
assertEquals(Boolean.TRUE, model.get("model"));
createFactory(new ModelHandler(), "model", Model.class).initModel(webRequest, mavContainer, requestMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("model"));
}
@Test
public void createModelWithName() throws Exception {
ModelMap model = createFactory(new ModelHandler(), "modelWithName").createModel(webRequest, requestMethod);
assertEquals(Boolean.TRUE, model.get("name"));
createFactory(new ModelHandler(), "modelWithName").initModel(webRequest, mavContainer, requestMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("name"));
}
@Test
public void createModelWithDefaultName() throws Exception {
ModelMap model = createFactory(new ModelHandler(), "modelWithDefaultName").createModel(webRequest, requestMethod);
assertEquals(Boolean.TRUE, model.get("boolean"));
createFactory(new ModelHandler(), "modelWithDefaultName").initModel(webRequest, mavContainer, requestMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("boolean"));
}
@Test
public void createModelWithExistingName() throws Exception {
ModelMap model = createFactory(new ModelHandler(), "modelWithName").createModel(webRequest, requestMethod);
assertEquals(Boolean.TRUE, model.get("name"));
createFactory(new ModelHandler(), "modelWithName").initModel(webRequest, mavContainer, requestMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("name"));
}
@Test
public void createModelWithNullAttribute() throws Exception {
ModelMap model = createFactory(new ModelHandler(), "modelWithNullAttribute").createModel(webRequest, requestMethod);
assertTrue(model.containsKey("name"));
assertNull(model.get("name"));
createFactory(new ModelHandler(), "modelWithNullAttribute").initModel(webRequest, mavContainer, requestMethod);
assertTrue(mavContainer.containsAttribute("name"));
assertNull(mavContainer.getAttribute("name"));
}
@Test
......@@ -114,8 +116,8 @@ public class ModelFactoryTests {
// Query attribute to associate it with the handler type
assertTrue(handlerSessionAttributeStore.isHandlerSessionAttribute("sessionAttr", null));
ModelMap model = createFactory(new ModelHandler(), "model", Model.class).createModel(webRequest, requestMethod);
assertEquals("sessionAttrValue", model.get("sessionAttr"));
createFactory(new ModelHandler(), "model", Model.class).initModel(webRequest, mavContainer, requestMethod);
assertEquals("sessionAttrValue", mavContainer.getAttribute("sessionAttr"));
}
@Test
......@@ -128,8 +130,7 @@ public class ModelFactoryTests {
String attrName = "attr1";
Object attrValue = new Object();
ModelMap actualModel = new ExtendedModelMap();
actualModel.addAttribute(attrName, attrValue);
mavContainer.addAttribute(attrName, attrValue);
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
......@@ -138,11 +139,11 @@ public class ModelFactoryTests {
replay(binderFactory);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttributeStore);
modelFactory.updateAttributes(webRequest, new SimpleSessionStatus(), actualModel, null);
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus());
assertEquals(attrValue, actualModel.remove(attrName));
assertSame(dataBinder.getBindingResult(), actualModel.remove(bindingResultKey(attrName)));
assertEquals(0, actualModel.size());
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
assertEquals(0, mavContainer.getModel().size());
verify(binderFactory);
}
......
......@@ -24,14 +24,12 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Test fixture for {@link ErrorsMethodArgumentResolver} unit tests.
......@@ -66,15 +64,15 @@ public class ErrorsMethodHandlerArgumentResolverTests {
WebDataBinder dataBinder = new WebDataBinder(new Object(), "attr");
BindingResult bindingResult = dataBinder.getBindingResult();
ModelMap model = new ExtendedModelMap();
model.addAttribute("ignore1", "value1");
model.addAttribute("ignore2", "value2");
model.addAttribute("ignore3", "value3");
model.addAttribute("ignore4", "value4");
model.addAttribute("ignore5", "value5");
model.addAllAttributes(bindingResult.getModel()); // Predictable iteration order of model keys important!
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAttribute("ignore1", "value1");
mavContainer.addAttribute("ignore2", "value2");
mavContainer.addAttribute("ignore3", "value3");
mavContainer.addAttribute("ignore4", "value4");
mavContainer.addAttribute("ignore5", "value5");
mavContainer.addAllAttributes(bindingResult.getModel()); // Predictable iteration order of model keys important!
Object actual = resolver.resolveArgument(errorsParam, model, webRequest, null);
Object actual = resolver.resolveArgument(errorsParam, mavContainer, webRequest, null);
assertSame(actual, bindingResult);
}
......@@ -89,14 +87,14 @@ public class ErrorsMethodHandlerArgumentResolverTests {
WebDataBinder binder2 = new WebDataBinder(target1, "attr2");
BindingResult bindingResult2 = binder2.getBindingResult();
ModelMap model = new ExtendedModelMap();
model.addAttribute("attr1", target1);
model.addAttribute("attr2", target2);
model.addAttribute("filler", "fillerValue");
model.addAllAttributes(bindingResult1.getModel());
model.addAllAttributes(bindingResult2.getModel());
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAttribute("attr1", target1);
mavContainer.addAttribute("attr2", target2);
mavContainer.addAttribute("filler", "fillerValue");
mavContainer.addAllAttributes(bindingResult1.getModel());
mavContainer.addAllAttributes(bindingResult2.getModel());
Object actual = resolver.resolveArgument(errorsParam, model, webRequest, null);
Object actual = resolver.resolveArgument(errorsParam, mavContainer, webRequest, null);
assertSame("Should resolve to the latest BindingResult added", actual, bindingResult2);
}
......@@ -106,16 +104,16 @@ public class ErrorsMethodHandlerArgumentResolverTests {
WebDataBinder dataBinder = new WebDataBinder(new Object(), "attr");
BindingResult bindingResult = dataBinder.getBindingResult();
ModelMap model = new ExtendedModelMap();
model.addAllAttributes(bindingResult.getModel());
model.addAttribute("ignore1", "value1");
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(bindingResult.getModel());
mavContainer.addAttribute("ignore1", "value1");
resolver.resolveArgument(errorsParam, model, webRequest, null);
resolver.resolveArgument(errorsParam, mavContainer, webRequest, null);
}
@Test(expected=IllegalStateException.class)
public void noBindingResult() throws Exception {
resolver.resolveArgument(errorsParam, new ExtendedModelMap(), webRequest, null);
resolver.resolveArgument(errorsParam, new ModelAndViewContainer(), webRequest, null);
}
@SuppressWarnings("unused")
......
......@@ -42,8 +42,6 @@ import org.junit.Test;
import org.springframework.beans.TestBean;
import org.springframework.core.MethodParameter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
......@@ -77,7 +75,7 @@ public class ModelAttributeMethodProcessorTests {
private MethodParameter notAnnotatedReturnParam;
private ModelMap model;
private ModelAndViewContainer mavContainer;
private NativeWebRequest webRequest;
......@@ -97,7 +95,7 @@ public class ModelAttributeMethodProcessorTests {
this.annotatedReturnParam = new MethodParameter(getClass().getDeclaredMethod("annotatedReturnValue"), -1);
this.notAnnotatedReturnParam = new MethodParameter(getClass().getDeclaredMethod("notAnnotatedReturnValue"), -1);
model = new ExtendedModelMap();
mavContainer = new ModelAndViewContainer();
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
}
......@@ -139,7 +137,7 @@ public class ModelAttributeMethodProcessorTests {
private void createBinderFromModelAttr(String expectedAttrName, MethodParameter param) throws Exception {
Object target = new TestBean();
model.addAttribute(expectedAttrName, target);
mavContainer.addAttribute(expectedAttrName, target);
WebDataBinder dataBinder = new WebRequestDataBinder(null);
......@@ -147,7 +145,7 @@ public class ModelAttributeMethodProcessorTests {
expect(binderFactory.createBinder(webRequest, target, expectedAttrName)).andReturn(dataBinder);
replay(binderFactory);
processor.resolveArgument(param, model, webRequest, binderFactory);
processor.resolveArgument(param, mavContainer, webRequest, binderFactory);
verify(binderFactory);
}
......@@ -160,7 +158,7 @@ public class ModelAttributeMethodProcessorTests {
expect(factory.createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName"))).andReturn(dataBinder);
replay(factory);
processor.resolveArgument(annotatedParam, model, webRequest, factory);
processor.resolveArgument(annotatedParam, mavContainer, webRequest, factory);
verify(factory);
}
......@@ -168,14 +166,14 @@ public class ModelAttributeMethodProcessorTests {
@Test
public void bindAndValidate() throws Exception {
Object target = new TestBean();
model.addAttribute("attrName", target);
mavContainer.addAttribute("attrName", target);
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target);
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
expect(binderFactory.createBinder(webRequest, target, "attrName")).andReturn(dataBinder);
replay(binderFactory);
processor.resolveArgument(annotatedParam, model, webRequest, binderFactory);
processor.resolveArgument(annotatedParam, mavContainer, webRequest, binderFactory);
assertTrue(dataBinder.isBindInvoked());
assertTrue(dataBinder.isValidateInvoked());
......@@ -184,7 +182,7 @@ public class ModelAttributeMethodProcessorTests {
@Test(expected=BindException.class)
public void bindAndFail() throws Exception {
Object target = new TestBean();
model.addAttribute(target);
mavContainer.getModel().addAttribute(target);
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target);
dataBinder.getBindingResult().reject("error");
......@@ -192,12 +190,12 @@ public class ModelAttributeMethodProcessorTests {
expect(binderFactory.createBinder(webRequest, target, "testBean")).andReturn(dataBinder);
replay(binderFactory);
processor.resolveArgument(notAnnotatedParam, model, webRequest, binderFactory);
processor.resolveArgument(notAnnotatedParam, mavContainer, webRequest, binderFactory);
}
@Test
public void handleAnnotatedReturnValue() throws Exception {
ModelAndViewContainer mavContainer = new ModelAndViewContainer(model);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
processor.handleReturnValue("expected", annotatedReturnParam, mavContainer, webRequest);
assertEquals("expected", mavContainer.getModel().get("modelAttrName"));
......@@ -206,7 +204,7 @@ public class ModelAttributeMethodProcessorTests {
@Test
public void handleNotAnnotatedReturnValue() throws Exception {
TestBean testBean = new TestBean("expected");
ModelAndViewContainer mavContainer = new ModelAndViewContainer(model);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
processor.handleReturnValue(testBean, notAnnotatedReturnParam, mavContainer, webRequest);
assertSame(testBean, mavContainer.getModel().get("testBean"));
......
......@@ -88,13 +88,13 @@ public class ModelMethodProcessorTests {
@Test
public void resolveArgumentValue() throws Exception {
ExtendedModelMap model = new ExtendedModelMap();
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
Object result = resolver.resolveArgument(modelParameter, model, webRequest, null);
assertSame(model, result);
Object result = resolver.resolveArgument(modelParameter, mavContainer, webRequest, null);
assertSame(mavContainer.getModel(), result);
result = resolver.resolveArgument(mapParameter, model, webRequest, null);
assertSame(model, result);
result = resolver.resolveArgument(mapParameter, mavContainer, webRequest, null);
assertSame(mavContainer.getModel(), result);
}
@Test
......
......@@ -78,18 +78,6 @@ public class HandlerMethodArgumentResolverCompositeTests {
this.composite.resolveArgument(paramString, null, null, null);
}
@Test
public void argResolverUsesResponse() throws Exception {
registerResolver(Integer.class, null, true);
assertTrue(this.composite.usesResponseArgument(paramInteger));
}
@Test
public void argResolverDoesntUseResponse() throws Exception {
registerResolver(Integer.class, null, false);
assertFalse(this.composite.usesResponseArgument(paramInteger));
}
protected StubArgumentResolver registerResolver(Class<?> supportedType, Object stubValue, boolean usesResponse) {
StubArgumentResolver resolver = new StubArgumentResolver(supportedType, stubValue, usesResponse);
this.composite.registerArgumentResolver(resolver);
......
......@@ -47,7 +47,7 @@ public class HandlerMethodReturnValueHandlerCompositeTests {
this.paramInteger = new MethodParameter(getClass().getDeclaredMethod("handleInteger"), -1);
this.paramString = new MethodParameter(getClass().getDeclaredMethod("handleString"), -1);
mavContainer = new ModelAndViewContainer(null);
mavContainer = new ModelAndViewContainer();
}
@Test
......@@ -81,18 +81,6 @@ public class HandlerMethodReturnValueHandlerCompositeTests {
registerReturnValueHandler(Integer.class, false);
this.composite.handleReturnValue("value", paramString, null, null);
}
@Test
public void returnValueHandlerUsesResponse() throws Exception {
registerReturnValueHandler(Integer.class, true);
assertTrue(this.composite.usesResponseArgument(paramInteger));
}
@Test
public void returnValueHandlerDosntUseResponse() throws Exception {
registerReturnValueHandler(Integer.class, false);
assertFalse(this.composite.usesResponseArgument(paramInteger));
}
protected StubReturnValueHandler registerReturnValueHandler(Class<?> returnType, boolean usesResponse) {
StubReturnValueHandler handler = new StubReturnValueHandler(returnType, usesResponse);
......
......@@ -17,7 +17,6 @@
package org.springframework.web.method.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Method;
......@@ -84,14 +83,6 @@ public class InvocableHandlerMethodTests {
assertEquals("intArg", resolver.getResolvedParameterNames().get(0).getParameterName());
}
@Test
public void usesResponseArgResolver() throws Exception {
InvocableHandlerMethod requestMethod = handlerMethod(new Handler(), "usesResponse", int.class);
registerResolver(int.class, 99, true);
assertTrue(requestMethod.usesResponseArgument());
}
private InvocableHandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes)
throws Exception {
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
......
......@@ -20,10 +20,8 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
/**
* Resolves a method argument from a stub value. Records all resolved parameters.
......@@ -58,8 +56,10 @@ public class StubArgumentResolver implements HandlerMethodArgumentResolver {
return parameter.getParameterType().equals(this.supportedType);
}
public Object resolveArgument(MethodParameter parameter, ModelMap model, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
this.resolvedParameters.add(parameter);
return this.stubValue;
}
......
......@@ -53,7 +53,7 @@ public class StubReturnValueHandler implements HandlerMethodReturnValueHandler {
NativeWebRequest webRequest) throws Exception {
this.unhandledReturnValue = returnValue;
if (returnValue != null) {
mavContainer.addModelAttribute(Conventions.getVariableName(returnValue), returnValue);
mavContainer.addAttribute(Conventions.getVariableName(returnValue), returnValue);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册