Polish
This commit is contained in:
parent
e62ada898b
commit
806e79b14b
|
@ -38,23 +38,24 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments annotated with {@code @ModelAttribute} and handles
|
* Resolve {@code @ModelAttribute} annotated method arguments and handle
|
||||||
* return values from methods annotated with {@code @ModelAttribute}.
|
* return values from {@code @ModelAttribute} annotated methods.
|
||||||
*
|
*
|
||||||
* <p>Model attributes are obtained from the model or if not found possibly
|
* <p>Model attributes are obtained from the model or created with a default
|
||||||
* created with a default constructor if it is available. Once created, the
|
* constructor (and then added to the model). Once created the attribute is
|
||||||
* attributed is populated with request data via data binding and also
|
* populated via data binding to Servlet request parameters. Validation may be
|
||||||
* validation may be applied if the argument is annotated with
|
* applied if the argument is annotated with {@code @javax.validation.Valid}.
|
||||||
* {@code @javax.validation.Valid}.
|
* or {@link @Validated}.
|
||||||
*
|
*
|
||||||
* <p>When this handler is created with {@code annotationNotRequired=true},
|
* <p>When this handler is created with {@code annotationNotRequired=true}
|
||||||
* any non-simple type argument and return value is regarded as a model
|
* any non-simple type argument and return value is regarded as a model
|
||||||
* attribute with or without the presence of an {@code @ModelAttribute}.
|
* attribute with or without the presence of an {@code @ModelAttribute}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
public class ModelAttributeMethodProcessor
|
||||||
|
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Class constructor.
|
||||||
* @param annotationNotRequired if "true", non-simple method arguments and
|
* @param annotationNotRequired if "true", non-simple method arguments and
|
||||||
* return values are considered model attributes with or without a
|
* return values are considered model attributes with or without a
|
||||||
* {@code @ModelAttribute} annotation.
|
* {@code @ModelAttribute} annotation.
|
||||||
|
@ -72,20 +74,14 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the parameter is annotated with {@link ModelAttribute}
|
* Returns {@code true} if the parameter is annotated with
|
||||||
* or in default resolution mode, and also if it is not a simple type.
|
* {@link ModelAttribute} or, if in default resolution mode, for any
|
||||||
|
* method parameter that is not a simple type.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
|
||||||
return true;
|
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
|
||||||
}
|
|
||||||
else if (this.annotationNotRequired) {
|
|
||||||
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,8 +98,8 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||||
|
|
||||||
String name = ModelFactory.getNameForParameter(parameter);
|
String name = ModelFactory.getNameForParameter(parameter);
|
||||||
Object attribute = (mavContainer.containsAttribute(name) ?
|
Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
|
||||||
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));
|
createAttribute(name, parameter, binderFactory, webRequest));
|
||||||
|
|
||||||
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
|
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
|
||||||
if (binder.getTarget() != null) {
|
if (binder.getTarget() != null) {
|
||||||
|
@ -182,19 +178,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {@code true} if there is a method-level {@code @ModelAttribute}
|
* Return {@code true} if there is a method-level {@code @ModelAttribute}
|
||||||
* or if it is a non-simple type when {@code annotationNotRequired=true}.
|
* or, in default resolution mode, for any return value type that is not
|
||||||
|
* a simple type.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsReturnType(MethodParameter returnType) {
|
public boolean supportsReturnType(MethodParameter returnType) {
|
||||||
if (returnType.getMethodAnnotation(ModelAttribute.class) != null) {
|
return (returnType.getMethodAnnotation(ModelAttribute.class) != null ||
|
||||||
return true;
|
this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType()));
|
||||||
}
|
|
||||||
else if (this.annotationNotRequired) {
|
|
||||||
return !BeanUtils.isSimpleProperty(returnType.getParameterType());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -45,14 +45,15 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides methods to initialize the {@link Model} before controller method
|
* Assist with initialization of the {@link Model} before controller method
|
||||||
* invocation and to update it afterwards.
|
* invocation and with updates to it after the invocation.
|
||||||
*
|
*
|
||||||
* <p>On initialization, the model is populated with attributes from the session
|
* <p>On initialization the model is populated with attributes temporarily
|
||||||
* and by invoking methods annotated with {@code @ModelAttribute}.
|
* stored in the session and through the invocation of {@code @ModelAttribute}
|
||||||
|
* methods.
|
||||||
*
|
*
|
||||||
* <p>On update, model attributes are synchronized with the session and also
|
* <p>On update model attributes are synchronized with the session and also
|
||||||
* {@link BindingResult} attributes are added where missing.
|
* {@link BindingResult} attributes are added if missing.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
@ -61,6 +62,7 @@ public final class ModelFactory {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(ModelFactory.class);
|
private static final Log logger = LogFactory.getLog(ModelFactory.class);
|
||||||
|
|
||||||
|
|
||||||
private final List<ModelMethod> modelMethods = new ArrayList<ModelMethod>();
|
private final List<ModelMethod> modelMethods = new ArrayList<ModelMethod>();
|
||||||
|
|
||||||
private final WebDataBinderFactory dataBinderFactory;
|
private final WebDataBinderFactory dataBinderFactory;
|
||||||
|
@ -70,22 +72,23 @@ public final class ModelFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance with the given {@code @ModelAttribute} methods.
|
* Create a new instance with the given {@code @ModelAttribute} methods.
|
||||||
* @param invocableMethods the {@code @ModelAttribute} methods to invoke
|
* @param handlerMethods the {@code @ModelAttribute} methods to invoke
|
||||||
* @param dataBinderFactory for preparation of {@link BindingResult} attributes
|
* @param binderFactory for preparation of {@link BindingResult} attributes
|
||||||
* @param sessionAttributesHandler for access to session attributes
|
* @param attributeHandler for access to session attributes
|
||||||
*/
|
*/
|
||||||
public ModelFactory(List<InvocableHandlerMethod> invocableMethods, WebDataBinderFactory dataBinderFactory,
|
public ModelFactory(List<InvocableHandlerMethod> handlerMethods,
|
||||||
SessionAttributesHandler sessionAttributesHandler) {
|
WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
|
||||||
|
|
||||||
if (invocableMethods != null) {
|
if (handlerMethods != null) {
|
||||||
for (InvocableHandlerMethod method : invocableMethods) {
|
for (InvocableHandlerMethod handlerMethod : handlerMethods) {
|
||||||
this.modelMethods.add(new ModelMethod(method));
|
this.modelMethods.add(new ModelMethod(handlerMethod));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.dataBinderFactory = dataBinderFactory;
|
this.dataBinderFactory = binderFactory;
|
||||||
this.sessionAttributesHandler = sessionAttributesHandler;
|
this.sessionAttributesHandler = attributeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the model in the following order:
|
* Populate the model in the following order:
|
||||||
* <ol>
|
* <ol>
|
||||||
|
@ -96,25 +99,26 @@ public final class ModelFactory {
|
||||||
* an exception if necessary.
|
* an exception if necessary.
|
||||||
* </ol>
|
* </ol>
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param mavContainer a container with the model to be initialized
|
* @param container a container with the model to be initialized
|
||||||
* @param handlerMethod the method for which the model is initialized
|
* @param handlerMethod the method for which the model is initialized
|
||||||
* @throws Exception may arise from {@code @ModelAttribute} methods
|
* @throws Exception may arise from {@code @ModelAttribute} methods
|
||||||
*/
|
*/
|
||||||
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
|
public void initModel(NativeWebRequest request, ModelAndViewContainer container,
|
||||||
throws Exception {
|
HandlerMethod handlerMethod) throws Exception {
|
||||||
|
|
||||||
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
|
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
|
||||||
mavContainer.mergeAttributes(sessionAttributes);
|
container.mergeAttributes(sessionAttributes);
|
||||||
|
|
||||||
invokeModelAttributeMethods(request, mavContainer);
|
invokeModelAttributeMethods(request, container);
|
||||||
|
|
||||||
for (String name : findSessionAttributeArguments(handlerMethod)) {
|
for (String name : findSessionAttributeArguments(handlerMethod)) {
|
||||||
if (!mavContainer.containsAttribute(name)) {
|
if (!container.containsAttribute(name)) {
|
||||||
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
|
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
|
throw new HttpSessionRequiredException(
|
||||||
|
"Expected session attribute '" + name + "'");
|
||||||
}
|
}
|
||||||
mavContainer.addAttribute(name, value);
|
container.addAttribute(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,30 +127,31 @@ public final class ModelFactory {
|
||||||
* Invoke model attribute methods to populate the model.
|
* Invoke model attribute methods to populate the model.
|
||||||
* Attributes are added only if not already present in the model.
|
* Attributes are added only if not already present in the model.
|
||||||
*/
|
*/
|
||||||
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
|
private void invokeModelAttributeMethods(NativeWebRequest request,
|
||||||
throws Exception {
|
ModelAndViewContainer container) throws Exception {
|
||||||
|
|
||||||
while (!this.modelMethods.isEmpty()) {
|
while (!this.modelMethods.isEmpty()) {
|
||||||
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
|
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
|
||||||
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
|
ModelAttribute annot = modelMethod.getMethodAnnotation(ModelAttribute.class);
|
||||||
if (mavContainer.containsAttribute(modelName)) {
|
String modelName = annot.value();
|
||||||
|
if (container.containsAttribute(modelName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
|
Object returnValue = modelMethod.invokeForRequest(request, container);
|
||||||
|
|
||||||
if (!attrMethod.isVoid()){
|
if (!modelMethod.isVoid()){
|
||||||
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
|
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
|
||||||
if (!mavContainer.containsAttribute(returnValueName)) {
|
if (!container.containsAttribute(returnValueName)) {
|
||||||
mavContainer.addAttribute(returnValueName, returnValue);
|
container.addAttribute(returnValueName, returnValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) {
|
private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
|
||||||
for (ModelMethod modelMethod : this.modelMethods) {
|
for (ModelMethod modelMethod : this.modelMethods) {
|
||||||
if (modelMethod.checkDependencies(mavContainer)) {
|
if (modelMethod.checkDependencies(container)) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Selected @ModelAttribute method " + modelMethod);
|
logger.trace("Selected @ModelAttribute method " + modelMethod);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +162,7 @@ public final class ModelFactory {
|
||||||
ModelMethod modelMethod = this.modelMethods.get(0);
|
ModelMethod modelMethod = this.modelMethods.get(0);
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Selected @ModelAttribute method (not present: " +
|
logger.trace("Selected @ModelAttribute method (not present: " +
|
||||||
modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod);
|
modelMethod.getUnresolvedDependencies(container)+ ") " + modelMethod);
|
||||||
}
|
}
|
||||||
this.modelMethods.remove(modelMethod);
|
this.modelMethods.remove(modelMethod);
|
||||||
return modelMethod;
|
return modelMethod;
|
||||||
|
@ -171,7 +176,8 @@ public final class ModelFactory {
|
||||||
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
|
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
|
||||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||||
String name = getNameForParameter(parameter);
|
String name = getNameForParameter(parameter);
|
||||||
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
|
Class<?> paramType = parameter.getParameterType();
|
||||||
|
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
|
||||||
result.add(name);
|
result.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,8 +195,8 @@ public final class ModelFactory {
|
||||||
*/
|
*/
|
||||||
public static String getNameForParameter(MethodParameter parameter) {
|
public static String getNameForParameter(MethodParameter parameter) {
|
||||||
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||||
String attrName = (annot != null) ? annot.value() : null;
|
String name = (annot != null) ? annot.value() : null;
|
||||||
return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);
|
return StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,7 +217,8 @@ public final class ModelFactory {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Method method = returnType.getMethod();
|
Method method = returnType.getMethod();
|
||||||
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
|
Class<?> containingClass = returnType.getContainingClass();
|
||||||
|
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
|
||||||
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
|
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,18 +227,18 @@ public final class ModelFactory {
|
||||||
* Promote model attributes listed as {@code @SessionAttributes} to the session.
|
* Promote model attributes listed as {@code @SessionAttributes} to the session.
|
||||||
* Add {@link BindingResult} attributes where necessary.
|
* Add {@link BindingResult} attributes where necessary.
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param mavContainer contains the model to update
|
* @param container contains the model to update
|
||||||
* @throws Exception if creating BindingResult attributes fails
|
* @throws Exception if creating BindingResult attributes fails
|
||||||
*/
|
*/
|
||||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
|
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
|
||||||
ModelMap defaultModel = mavContainer.getDefaultModel();
|
ModelMap defaultModel = container.getDefaultModel();
|
||||||
if (mavContainer.getSessionStatus().isComplete()){
|
if (container.getSessionStatus().isComplete()){
|
||||||
this.sessionAttributesHandler.cleanupAttributes(request);
|
this.sessionAttributesHandler.cleanupAttributes(request);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
|
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
|
||||||
}
|
}
|
||||||
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
|
if (!container.isRequestHandled() && container.getModel() == defaultModel) {
|
||||||
updateBindingResult(request, defaultModel);
|
updateBindingResult(request, defaultModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +255,7 @@ public final class ModelFactory {
|
||||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
|
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
|
||||||
|
|
||||||
if (!model.containsAttribute(bindingResultKey)) {
|
if (!model.containsAttribute(bindingResultKey)) {
|
||||||
WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name);
|
WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
|
||||||
model.put(bindingResultKey, dataBinder.getBindingResult());
|
model.put(bindingResultKey, dataBinder.getBindingResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
|
@ -51,56 +52,53 @@ import static org.mockito.BDDMockito.*;
|
||||||
*/
|
*/
|
||||||
public class ModelAttributeMethodProcessorTests {
|
public class ModelAttributeMethodProcessorTests {
|
||||||
|
|
||||||
|
private NativeWebRequest request;
|
||||||
|
|
||||||
|
private ModelAndViewContainer container;
|
||||||
|
|
||||||
private ModelAttributeMethodProcessor processor;
|
private ModelAttributeMethodProcessor processor;
|
||||||
|
|
||||||
private MethodParameter paramNamedValidModelAttr;
|
private MethodParameter paramNamedValidModelAttr;
|
||||||
|
|
||||||
private MethodParameter paramErrors;
|
private MethodParameter paramErrors;
|
||||||
|
|
||||||
private MethodParameter paramInt;
|
private MethodParameter paramInt;
|
||||||
|
|
||||||
private MethodParameter paramModelAttr;
|
private MethodParameter paramModelAttr;
|
||||||
|
|
||||||
private MethodParameter paramNonSimpleType;
|
private MethodParameter paramNonSimpleType;
|
||||||
|
|
||||||
private MethodParameter returnParamNamedModelAttr;
|
private MethodParameter returnParamNamedModelAttr;
|
||||||
|
|
||||||
private MethodParameter returnParamNonSimpleType;
|
private MethodParameter returnParamNonSimpleType;
|
||||||
|
|
||||||
private ModelAndViewContainer mavContainer;
|
|
||||||
|
|
||||||
private NativeWebRequest webRequest;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
processor = new ModelAttributeMethodProcessor(false);
|
this.request = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
this.container = new ModelAndViewContainer();
|
||||||
|
this.processor = new ModelAttributeMethodProcessor(false);
|
||||||
|
|
||||||
Method method = ModelAttributeHandler.class.getDeclaredMethod("modelAttribute",
|
Method method = ModelAttributeHandler.class.getDeclaredMethod("modelAttribute",
|
||||||
TestBean.class, Errors.class, int.class, TestBean.class, TestBean.class);
|
TestBean.class, Errors.class, int.class, TestBean.class, TestBean.class);
|
||||||
|
|
||||||
paramNamedValidModelAttr = new MethodParameter(method, 0);
|
this.paramNamedValidModelAttr = new SynthesizingMethodParameter(method, 0);
|
||||||
paramErrors = new MethodParameter(method, 1);
|
this.paramErrors = new SynthesizingMethodParameter(method, 1);
|
||||||
paramInt = new MethodParameter(method, 2);
|
this.paramInt = new SynthesizingMethodParameter(method, 2);
|
||||||
paramModelAttr = new MethodParameter(method, 3);
|
this.paramModelAttr = new SynthesizingMethodParameter(method, 3);
|
||||||
paramNonSimpleType = new MethodParameter(method, 4);
|
this.paramNonSimpleType = new SynthesizingMethodParameter(method, 4);
|
||||||
|
|
||||||
returnParamNamedModelAttr = new MethodParameter(getClass().getDeclaredMethod("annotatedReturnValue"), -1);
|
method = getClass().getDeclaredMethod("annotatedReturnValue");
|
||||||
returnParamNonSimpleType = new MethodParameter(getClass().getDeclaredMethod("notAnnotatedReturnValue"), -1);
|
this.returnParamNamedModelAttr = new MethodParameter(method, -1);
|
||||||
|
|
||||||
mavContainer = new ModelAndViewContainer();
|
method = getClass().getDeclaredMethod("notAnnotatedReturnValue");
|
||||||
|
this.returnParamNonSimpleType = new MethodParameter(method, -1);
|
||||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportedParameters() throws Exception {
|
public void supportedParameters() throws Exception {
|
||||||
// Only @ModelAttribute arguments
|
assertTrue(this.processor.supportsParameter(this.paramNamedValidModelAttr));
|
||||||
assertTrue(processor.supportsParameter(paramNamedValidModelAttr));
|
assertTrue(this.processor.supportsParameter(this.paramModelAttr));
|
||||||
assertTrue(processor.supportsParameter(paramModelAttr));
|
|
||||||
|
|
||||||
assertFalse(processor.supportsParameter(paramErrors));
|
assertFalse(this.processor.supportsParameter(this.paramErrors));
|
||||||
assertFalse(processor.supportsParameter(paramInt));
|
assertFalse(this.processor.supportsParameter(this.paramInt));
|
||||||
assertFalse(processor.supportsParameter(paramNonSimpleType));
|
assertFalse(this.processor.supportsParameter(this.paramNonSimpleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -108,135 +106,127 @@ public class ModelAttributeMethodProcessorTests {
|
||||||
processor = new ModelAttributeMethodProcessor(true);
|
processor = new ModelAttributeMethodProcessor(true);
|
||||||
|
|
||||||
// Only non-simple types, even if not annotated
|
// Only non-simple types, even if not annotated
|
||||||
assertTrue(processor.supportsParameter(paramNamedValidModelAttr));
|
assertTrue(this.processor.supportsParameter(this.paramNamedValidModelAttr));
|
||||||
assertTrue(processor.supportsParameter(paramErrors));
|
assertTrue(this.processor.supportsParameter(this.paramErrors));
|
||||||
assertTrue(processor.supportsParameter(paramModelAttr));
|
assertTrue(this.processor.supportsParameter(this.paramModelAttr));
|
||||||
assertTrue(processor.supportsParameter(paramNonSimpleType));
|
assertTrue(this.processor.supportsParameter(this.paramNonSimpleType));
|
||||||
|
|
||||||
assertFalse(processor.supportsParameter(paramInt));
|
assertFalse(this.processor.supportsParameter(this.paramInt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportedReturnTypes() throws Exception {
|
public void supportedReturnTypes() throws Exception {
|
||||||
processor = new ModelAttributeMethodProcessor(false);
|
processor = new ModelAttributeMethodProcessor(false);
|
||||||
assertTrue(processor.supportsReturnType(returnParamNamedModelAttr));
|
assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr));
|
||||||
assertFalse(processor.supportsReturnType(returnParamNonSimpleType));
|
assertFalse(this.processor.supportsReturnType(returnParamNonSimpleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportedReturnTypesInDefaultResolutionMode() throws Exception {
|
public void supportedReturnTypesInDefaultResolutionMode() throws Exception {
|
||||||
processor = new ModelAttributeMethodProcessor(true);
|
processor = new ModelAttributeMethodProcessor(true);
|
||||||
assertTrue(processor.supportsReturnType(returnParamNamedModelAttr));
|
assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr));
|
||||||
assertTrue(processor.supportsReturnType(returnParamNonSimpleType));
|
assertTrue(this.processor.supportsReturnType(returnParamNonSimpleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindExceptionRequired() throws Exception {
|
public void bindExceptionRequired() throws Exception {
|
||||||
assertTrue(processor.isBindExceptionRequired(null, paramNonSimpleType));
|
assertTrue(this.processor.isBindExceptionRequired(null, this.paramNonSimpleType));
|
||||||
|
assertFalse(this.processor.isBindExceptionRequired(null, this.paramNamedValidModelAttr));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindExceptionNotRequired() throws Exception {
|
public void resolveArgumentFromModel() throws Exception {
|
||||||
assertFalse(processor.isBindExceptionRequired(null, paramNamedValidModelAttr));
|
testGetAttributeFromModel("attrName", this.paramNamedValidModelAttr);
|
||||||
}
|
testGetAttributeFromModel("testBean", this.paramModelAttr);
|
||||||
|
testGetAttributeFromModel("testBean", this.paramNonSimpleType);
|
||||||
@Test
|
|
||||||
public void resovleArgumentFromModel() throws Exception {
|
|
||||||
getAttributeFromModel("attrName", paramNamedValidModelAttr);
|
|
||||||
getAttributeFromModel("testBean", paramModelAttr);
|
|
||||||
getAttributeFromModel("testBean", paramNonSimpleType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getAttributeFromModel(String expectedAttributeName, MethodParameter param) throws Exception {
|
|
||||||
Object target = new TestBean();
|
|
||||||
mavContainer.addAttribute(expectedAttributeName, target);
|
|
||||||
|
|
||||||
WebDataBinder dataBinder = new WebRequestDataBinder(target);
|
|
||||||
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
|
||||||
given(factory.createBinder(webRequest, target, expectedAttributeName)).willReturn(dataBinder);
|
|
||||||
|
|
||||||
processor.resolveArgument(param, mavContainer, webRequest, factory);
|
|
||||||
verify(factory).createBinder(webRequest, target, expectedAttributeName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resovleArgumentViaDefaultConstructor() throws Exception {
|
public void resovleArgumentViaDefaultConstructor() throws Exception {
|
||||||
WebDataBinder dataBinder = new WebRequestDataBinder(null);
|
WebDataBinder dataBinder = new WebRequestDataBinder(null);
|
||||||
|
|
||||||
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
||||||
given(factory.createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName"))).willReturn(dataBinder);
|
given(factory.createBinder(anyObject(), notNull(), eq("attrName"))).willReturn(dataBinder);
|
||||||
|
|
||||||
processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, factory);
|
this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory);
|
||||||
|
verify(factory).createBinder(anyObject(), notNull(), eq("attrName"));
|
||||||
verify(factory).createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveArgumentValidation() throws Exception {
|
public void resolveArgumentValidation() throws Exception {
|
||||||
String name = "attrName";
|
String name = "attrName";
|
||||||
Object target = new TestBean();
|
Object target = new TestBean();
|
||||||
mavContainer.addAttribute(name, target);
|
this.container.addAttribute(name, target);
|
||||||
|
|
||||||
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
|
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(webRequest, target, name)).willReturn(dataBinder);
|
given(factory.createBinder(this.request, target, name)).willReturn(dataBinder);
|
||||||
|
|
||||||
processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, binderFactory);
|
this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory);
|
||||||
|
|
||||||
assertTrue(dataBinder.isBindInvoked());
|
assertTrue(dataBinder.isBindInvoked());
|
||||||
assertTrue(dataBinder.isValidateInvoked());
|
assertTrue(dataBinder.isValidateInvoked());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BindException.class)
|
@Test(expected = BindException.class)
|
||||||
public void resovleArgumentBindException() throws Exception {
|
public void resolveArgumentBindException() throws Exception {
|
||||||
String name = "testBean";
|
String name = "testBean";
|
||||||
Object target = new TestBean();
|
Object target = new TestBean();
|
||||||
mavContainer.getModel().addAttribute(target);
|
this.container.getModel().addAttribute(target);
|
||||||
|
|
||||||
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
|
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
|
||||||
dataBinder.getBindingResult().reject("error");
|
dataBinder.getBindingResult().reject("error");
|
||||||
|
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(webRequest, target, name)).willReturn(dataBinder);
|
given(binderFactory.createBinder(this.request, target, name)).willReturn(dataBinder);
|
||||||
|
|
||||||
processor.resolveArgument(paramNonSimpleType, mavContainer, webRequest, binderFactory);
|
this.processor.resolveArgument(this.paramNonSimpleType, this.container, this.request, binderFactory);
|
||||||
verify(binderFactory).createBinder(webRequest, target, name);
|
verify(binderFactory).createBinder(this.request, target, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-9378
|
@Test // SPR-9378
|
||||||
public void resolveArgumentOrdering() throws Exception {
|
public void resolveArgumentOrdering() throws Exception {
|
||||||
String name = "testBean";
|
String name = "testBean";
|
||||||
Object testBean = new TestBean(name);
|
Object testBean = new TestBean(name);
|
||||||
mavContainer.addAttribute(name, testBean);
|
this.container.addAttribute(name, testBean);
|
||||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, testBean);
|
this.container.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, testBean);
|
||||||
|
|
||||||
Object anotherTestBean = new TestBean();
|
Object anotherTestBean = new TestBean();
|
||||||
mavContainer.addAttribute("anotherTestBean", anotherTestBean);
|
this.container.addAttribute("anotherTestBean", anotherTestBean);
|
||||||
|
|
||||||
StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name);
|
StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name);
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(webRequest, testBean, name)).willReturn(dataBinder);
|
given(binderFactory.createBinder(this.request, testBean, name)).willReturn(dataBinder);
|
||||||
|
|
||||||
processor.resolveArgument(paramModelAttr, mavContainer, webRequest, binderFactory);
|
this.processor.resolveArgument(this.paramModelAttr, this.container, this.request, binderFactory);
|
||||||
|
|
||||||
assertSame("Resolved attribute should be updated to be last in the order",
|
Object[] values = this.container.getModel().values().toArray();
|
||||||
testBean, mavContainer.getModel().values().toArray()[1]);
|
assertSame("Resolved attribute should be updated to be last", testBean, values[1]);
|
||||||
assertSame("BindingResult of resolved attribute should be last in the order",
|
assertSame("BindingResult of resolved attr should be last", dataBinder.getBindingResult(), values[2]);
|
||||||
dataBinder.getBindingResult(), mavContainer.getModel().values().toArray()[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handleAnnotatedReturnValue() throws Exception {
|
public void handleAnnotatedReturnValue() throws Exception {
|
||||||
processor.handleReturnValue("expected", returnParamNamedModelAttr, mavContainer, webRequest);
|
this.processor.handleReturnValue("expected", this.returnParamNamedModelAttr, this.container, this.request);
|
||||||
assertEquals("expected", mavContainer.getModel().get("modelAttrName"));
|
assertEquals("expected", this.container.getModel().get("modelAttrName"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handleNotAnnotatedReturnValue() throws Exception {
|
public void handleNotAnnotatedReturnValue() throws Exception {
|
||||||
TestBean testBean = new TestBean("expected");
|
TestBean testBean = new TestBean("expected");
|
||||||
processor.handleReturnValue(testBean, returnParamNonSimpleType, mavContainer, webRequest);
|
this.processor.handleReturnValue(testBean, this.returnParamNonSimpleType, this.container, this.request);
|
||||||
|
assertSame(testBean, this.container.getModel().get("testBean"));
|
||||||
|
}
|
||||||
|
|
||||||
assertSame(testBean, mavContainer.getModel().get("testBean"));
|
|
||||||
|
private void testGetAttributeFromModel(String expectedAttrName, MethodParameter param) throws Exception {
|
||||||
|
Object target = new TestBean();
|
||||||
|
this.container.addAttribute(expectedAttrName, target);
|
||||||
|
|
||||||
|
WebDataBinder dataBinder = new WebRequestDataBinder(target);
|
||||||
|
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
||||||
|
given(factory.createBinder(this.request, target, expectedAttrName)).willReturn(dataBinder);
|
||||||
|
|
||||||
|
this.processor.resolveArgument(param, this.container, this.request, factory);
|
||||||
|
verify(factory).createBinder(this.request, target, expectedAttrName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,6 +236,7 @@ public class ModelAttributeMethodProcessorTests {
|
||||||
|
|
||||||
private boolean validateInvoked;
|
private boolean validateInvoked;
|
||||||
|
|
||||||
|
|
||||||
public StubRequestDataBinder(Object target, String objectName) {
|
public StubRequestDataBinder(Object target, String objectName) {
|
||||||
super(target, objectName);
|
super(target, objectName);
|
||||||
}
|
}
|
||||||
|
@ -285,13 +276,17 @@ public class ModelAttributeMethodProcessorTests {
|
||||||
private static class ModelAttributeHandler {
|
private static class ModelAttributeHandler {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void modelAttribute(@ModelAttribute("attrName") @Valid TestBean annotatedAttr, Errors errors,
|
public void modelAttribute(
|
||||||
int intArg, @ModelAttribute TestBean defaultNameAttr, TestBean notAnnotatedAttr) {
|
@ModelAttribute("attrName") @Valid TestBean annotatedAttr,
|
||||||
|
Errors errors,
|
||||||
|
int intArg,
|
||||||
|
@ModelAttribute TestBean defaultNameAttr,
|
||||||
|
TestBean notAnnotatedAttr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ModelAttribute("modelAttrName")
|
@ModelAttribute("modelAttrName") @SuppressWarnings("unused")
|
||||||
private String annotatedReturnValue() {
|
private String annotatedReturnValue() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,19 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.web.method.annotation;
|
package org.springframework.web.method.annotation;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
import static org.mockito.BDDMockito.mock;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -43,10 +36,19 @@ import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
|
||||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.BDDMockito.mock;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text fixture for {@link ModelFactory} tests.
|
* Text fixture for {@link ModelFactory} tests.
|
||||||
|
@ -55,103 +57,92 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
*/
|
*/
|
||||||
public class ModelFactoryTests {
|
public class ModelFactoryTests {
|
||||||
|
|
||||||
|
private NativeWebRequest webRequest;
|
||||||
|
|
||||||
|
private SessionAttributesHandler attributeHandler;
|
||||||
|
|
||||||
|
private SessionAttributeStore attributeStore;
|
||||||
|
|
||||||
private TestController controller = new TestController();
|
private TestController controller = new TestController();
|
||||||
|
|
||||||
private InvocableHandlerMethod handleMethod;
|
private ModelAndViewContainer mavContainer;
|
||||||
|
|
||||||
private InvocableHandlerMethod handleSessionAttrMethod;
|
|
||||||
|
|
||||||
private SessionAttributesHandler sessionAttrsHandler;
|
|
||||||
|
|
||||||
private SessionAttributeStore sessionAttributeStore;
|
|
||||||
|
|
||||||
private NativeWebRequest webRequest;
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
this.controller = new TestController();
|
|
||||||
|
|
||||||
Method method = TestController.class.getDeclaredMethod("handle");
|
|
||||||
this.handleMethod = new InvocableHandlerMethod(this.controller, method);
|
|
||||||
|
|
||||||
method = TestController.class.getDeclaredMethod("handleSessionAttr", String.class);
|
|
||||||
this.handleSessionAttrMethod = new InvocableHandlerMethod(this.controller, method);
|
|
||||||
|
|
||||||
this.sessionAttributeStore = new DefaultSessionAttributeStore();
|
|
||||||
this.sessionAttrsHandler = new SessionAttributesHandler(TestController.class, this.sessionAttributeStore);
|
|
||||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
this.attributeStore = new DefaultSessionAttributeStore();
|
||||||
|
this.attributeHandler = new SessionAttributesHandler(TestController.class, this.attributeStore);
|
||||||
|
this.controller = new TestController();
|
||||||
|
this.mavContainer = new ModelAndViewContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modelAttributeMethod() throws Exception {
|
public void modelAttributeMethod() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
HandlerMethod handlerMethod = createHandlerMethod("handle");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
|
||||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("modelAttr"));
|
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("modelAttr"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modelAttributeMethodWithExplicitName() throws Exception {
|
public void modelAttributeMethodWithExplicitName() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
|
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
HandlerMethod handlerMethod = createHandlerMethod("handle");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
|
||||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("name"));
|
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modelAttributeMethodWithNameByConvention() throws Exception {
|
public void modelAttributeMethodWithNameByConvention() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
|
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
HandlerMethod handlerMethod = createHandlerMethod("handle");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
|
||||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("boolean"));
|
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("boolean"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modelAttributeMethodWithNullReturnValue() throws Exception {
|
public void modelAttributeMethodWithNullReturnValue() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("nullModelAttr");
|
ModelFactory modelFactory = createModelFactory("nullModelAttr");
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
HandlerMethod handlerMethod = createHandlerMethod("handle");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
|
||||||
assertTrue(mavContainer.containsAttribute("name"));
|
assertTrue(this.mavContainer.containsAttribute("name"));
|
||||||
assertNull(mavContainer.getModel().get("name"));
|
assertNull(this.mavContainer.getModel().get("name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sessionAttribute() throws Exception {
|
public void sessionAttribute() throws Exception {
|
||||||
this.sessionAttributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
|
this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
|
||||||
|
|
||||||
// Resolve successfully handler session attribute once
|
|
||||||
assertTrue(sessionAttrsHandler.isHandlerSessionAttribute("sessionAttr", null));
|
|
||||||
|
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
HandlerMethod handlerMethod = createHandlerMethod("handle");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
|
||||||
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
|
assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sessionAttributeNotPresent() throws Exception {
|
public void sessionAttributeNotPresent() throws Exception {
|
||||||
ModelFactory modelFactory = new ModelFactory(null, null, this.sessionAttrsHandler);
|
ModelFactory modelFactory = new ModelFactory(null, null, this.attributeHandler);
|
||||||
|
HandlerMethod handlerMethod = createHandlerMethod("handleSessionAttr", String.class);
|
||||||
try {
|
try {
|
||||||
modelFactory.initModel(this.webRequest, new ModelAndViewContainer(), this.handleSessionAttrMethod);
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
fail("Expected HttpSessionRequiredException");
|
fail("Expected HttpSessionRequiredException");
|
||||||
}
|
}
|
||||||
catch (HttpSessionRequiredException e) {
|
catch (HttpSessionRequiredException e) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sessionAttributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
|
// Now add attribute and try again
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
|
||||||
modelFactory.initModel(this.webRequest, mavContainer, this.handleSessionAttrMethod);
|
|
||||||
|
|
||||||
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
|
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
|
||||||
|
assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -165,11 +156,12 @@ public class ModelFactoryTests {
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(this.webRequest, command, commandName)).willReturn(dataBinder);
|
given(binderFactory.createBinder(this.webRequest, command, commandName)).willReturn(dataBinder);
|
||||||
|
|
||||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler);
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
|
||||||
modelFactory.updateModel(this.webRequest, container);
|
modelFactory.updateModel(this.webRequest, container);
|
||||||
|
|
||||||
assertEquals(command, container.getModel().get(commandName));
|
assertEquals(command, container.getModel().get(commandName));
|
||||||
assertSame(dataBinder.getBindingResult(), container.getModel().get(bindingResultKey(commandName)));
|
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + commandName;
|
||||||
|
assertSame(dataBinder.getBindingResult(), container.getModel().get(bindingResultKey));
|
||||||
assertEquals(2, container.getModel().size());
|
assertEquals(2, container.getModel().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,11 +176,11 @@ public class ModelFactoryTests {
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
|
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
|
||||||
|
|
||||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler);
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
|
||||||
modelFactory.updateModel(this.webRequest, container);
|
modelFactory.updateModel(this.webRequest, container);
|
||||||
|
|
||||||
assertEquals(attribute, container.getModel().get(attributeName));
|
assertEquals(attribute, container.getModel().get(attributeName));
|
||||||
assertEquals(attribute, this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName));
|
assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -198,9 +190,7 @@ public class ModelFactoryTests {
|
||||||
ModelAndViewContainer container = new ModelAndViewContainer();
|
ModelAndViewContainer container = new ModelAndViewContainer();
|
||||||
container.addAttribute(attributeName, attribute);
|
container.addAttribute(attributeName, attribute);
|
||||||
|
|
||||||
// Store and resolve once (to be "remembered")
|
this.attributeStore.storeAttribute(this.webRequest, attributeName, attribute);
|
||||||
this.sessionAttributeStore.storeAttribute(this.webRequest, attributeName, attribute);
|
|
||||||
this.sessionAttrsHandler.isHandlerSessionAttribute(attributeName, null);
|
|
||||||
|
|
||||||
WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName);
|
WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName);
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
|
@ -208,11 +198,11 @@ public class ModelFactoryTests {
|
||||||
|
|
||||||
container.getSessionStatus().setComplete();
|
container.getSessionStatus().setComplete();
|
||||||
|
|
||||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler);
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
|
||||||
modelFactory.updateModel(this.webRequest, container);
|
modelFactory.updateModel(this.webRequest, container);
|
||||||
|
|
||||||
assertEquals(attribute, container.getModel().get(attributeName));
|
assertEquals(attribute, container.getModel().get(attributeName));
|
||||||
assertNull(this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName));
|
assertNull(this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-12542
|
// SPR-12542
|
||||||
|
@ -233,33 +223,33 @@ public class ModelFactoryTests {
|
||||||
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
|
||||||
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
|
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
|
||||||
|
|
||||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler);
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
|
||||||
modelFactory.updateModel(this.webRequest, container);
|
modelFactory.updateModel(this.webRequest, container);
|
||||||
|
|
||||||
assertEquals(queryParam, container.getModel().get(queryParamName));
|
assertEquals(queryParam, container.getModel().get(queryParamName));
|
||||||
assertEquals(1, container.getModel().size());
|
assertEquals(1, container.getModel().size());
|
||||||
assertEquals(attribute, this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName));
|
assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String bindingResultKey(String key) {
|
private ModelFactory createModelFactory(String methodName, Class<?>... parameterTypes) throws Exception {
|
||||||
return BindingResult.MODEL_KEY_PREFIX + key;
|
HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
|
||||||
|
resolvers.addResolver(new ModelMethodProcessor());
|
||||||
|
|
||||||
|
InvocableHandlerMethod modelMethod = createHandlerMethod(methodName, parameterTypes);
|
||||||
|
modelMethod.setHandlerMethodArgumentResolvers(resolvers);
|
||||||
|
modelMethod.setDataBinderFactory(null);
|
||||||
|
modelMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||||
|
|
||||||
|
return new ModelFactory(Collections.singletonList(modelMethod), null, this.attributeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelFactory createModelFactory(String methodName, Class<?>... parameterTypes) throws Exception{
|
private InvocableHandlerMethod createHandlerMethod(String methodName, Class<?>... paramTypes) throws Exception {
|
||||||
Method method = TestController.class.getMethod(methodName, parameterTypes);
|
Method method = this.controller.getClass().getMethod(methodName, paramTypes);
|
||||||
|
return new InvocableHandlerMethod(this.controller, method);
|
||||||
HandlerMethodArgumentResolverComposite argResolvers = new HandlerMethodArgumentResolverComposite();
|
|
||||||
argResolvers.addResolver(new ModelMethodProcessor());
|
|
||||||
|
|
||||||
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(this.controller, method);
|
|
||||||
handlerMethod.setHandlerMethodArgumentResolvers(argResolvers);
|
|
||||||
handlerMethod.setDataBinderFactory(null);
|
|
||||||
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
|
||||||
|
|
||||||
return new ModelFactory(Arrays.asList(handlerMethod), null, this.sessionAttrsHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")
|
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")
|
||||||
private static class TestController {
|
private static class TestController {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue