Allow binding=false on @ModelAttribute

Issue: SPR-13402
This commit is contained in:
Rossen Stoyanchev
2016-01-26 21:12:21 -05:00
parent 806e79b14b
commit 2e7470b27f
8 changed files with 164 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -22,6 +22,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.ui.Model;
/**
@@ -49,6 +50,7 @@ import org.springframework.ui.Model;
* access to a {@link Model} argument.
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 2.5
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -56,6 +58,12 @@ import org.springframework.ui.Model;
@Documented
public @interface ModelAttribute {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the model attribute to bind to.
* <p>The default model attribute name is inferred from the declared
@@ -63,7 +71,19 @@ public @interface ModelAttribute {
* based on the non-qualified class name:
* e.g. "orderAddress" for class "mypackage.OrderAddress",
* or "orderAddressList" for "List&lt;mypackage.OrderAddress&gt;".
* @since 4.3
*/
String value() default "";
@AliasFor("value")
String name() default "";
/**
* Allows declaring data binding disabled directly on an
* {@code @ModelAttribute} method parameter or on the attribute returned from
* an {@code @ModelAttribute} method, both of which would prevent data
* binding for that attribute.
* <p>By default this is set to "true" in which case data binding applies.
* Set this to "false" to disable data binding.
*/
boolean binding() default true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 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.
@@ -101,9 +101,18 @@ public class ModelAttributeMethodProcessor
Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
createAttribute(name, parameter, binderFactory, webRequest));
if (!mavContainer.isBindingDisabled(name)) {
ModelAttribute annotation = parameter.getParameterAnnotation(ModelAttribute.class);
if (annotation != null && !annotation.binding()) {
mavContainer.setBindingDisabled(name);
}
}
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, webRequest);
if (!mavContainer.isBindingDisabled(name)) {
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());

View File

@@ -132,9 +132,11 @@ public final class ModelFactory {
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
ModelAttribute annot = modelMethod.getMethodAnnotation(ModelAttribute.class);
String modelName = annot.value();
if (container.containsAttribute(modelName)) {
ModelAttribute annotation = modelMethod.getMethodAnnotation(ModelAttribute.class);
if (container.containsAttribute(annotation.name())) {
if (!annotation.binding()) {
container.setBindingDisabled(annotation.name());
}
continue;
}
@@ -142,6 +144,9 @@ public final class ModelFactory {
if (!modelMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!annotation.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}

View File

@@ -16,7 +16,9 @@
package org.springframework.web.method.support;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
@@ -55,6 +57,9 @@ public class ModelAndViewContainer {
private boolean redirectModelScenario = false;
/* Names of attributes with binding disabled */
private final Set<String> bindingDisabledAttributes = new HashSet<String>(4);
private HttpStatus status;
private final SessionStatus sessionStatus = new SimpleSessionStatus();
@@ -133,6 +138,23 @@ public class ModelAndViewContainer {
}
}
/**
* Register an attribute for which data binding should not occur, for example
* corresponding to an {@code @ModelAttribute(binding=false)} declaration.
* @param attributeName the name of the attribute
* @since 4.3
*/
public void setBindingDisabled(String attributeName) {
this.bindingDisabledAttributes.add(attributeName);
}
/**
* Whether binding is disabled for the given model attribute.
*/
public boolean isBindingDisabled(String name) {
return this.bindingDisabledAttributes.contains(name);
}
/**
* Whether to use the default model or the redirect model.
*/