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