SPR-8214 Javadoc and polish
This commit is contained in:
@@ -18,33 +18,34 @@ package org.springframework.web.method.annotation.support;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link CookieValue @CookieValue}.
|
||||
*
|
||||
* A base abstract class to resolve method arguments annotated with @{@link CookieValue}. Subclasses must define how
|
||||
* to extract the cookie value from the request.
|
||||
*
|
||||
* <p>An @{@link CookieValue} is a named value that is resolved from a cookie. It has a required flag and a
|
||||
* default value to fall back on when the cookie does not exist. See the base class
|
||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
||||
*
|
||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved cookie values that don't yet match
|
||||
* the method parameter type.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
*/
|
||||
public AbstractCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
public UrlPathHelper getUrlPathHelper() {
|
||||
return urlPathHelper;
|
||||
}
|
||||
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(CookieValue.class);
|
||||
}
|
||||
@@ -55,18 +56,11 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodA
|
||||
return new CookieValueNamedValueInfo(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String cookieName) throws Exception {
|
||||
|
||||
throw new UnsupportedOperationException("@CookieValue not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String cookieName, MethodParameter parameter) {
|
||||
String paramTypeName = parameter.getParameterType().getName();
|
||||
throw new IllegalStateException(
|
||||
"Missing cookie value '" + cookieName + "' of type [" + parameter.getParameterType().getName() + "]");
|
||||
"Missing cookie named '" + cookieName + "' for method parameter type [" + paramTypeName + "]");
|
||||
}
|
||||
|
||||
private static class CookieValueNamedValueInfo extends NamedValueInfo {
|
||||
@@ -75,4 +69,4 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodA
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
@@ -34,8 +35,19 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Abstract base class for argument resolvers that resolve named values.
|
||||
*
|
||||
* Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and
|
||||
* path variables are examples of named values. Each may have a name, a required flag, and a default value.
|
||||
* <p>Subclasses define how to do the following:
|
||||
* <ul>
|
||||
* <li>Obtain named value information for a method parameter
|
||||
* <li>Resolve names into argument values
|
||||
* <li>Handle missing argument values when argument values are required
|
||||
* </ul>
|
||||
* <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions.
|
||||
* For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor.
|
||||
* <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't
|
||||
* match the method parameter type.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -49,15 +61,15 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
private Map<MethodParameter, NamedValueInfo> namedValueInfoCache =
|
||||
new ConcurrentHashMap<MethodParameter, NamedValueInfo>();
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
*/
|
||||
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -66,7 +78,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
|
||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||
|
||||
Object arg = resolveNamedValueArgument(webRequest, parameter, namedValueInfo.name);
|
||||
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
|
||||
|
||||
if (arg == null) {
|
||||
if (namedValueInfo.defaultValue != null) {
|
||||
@@ -87,74 +99,73 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the named value for the given method parameter.
|
||||
*/
|
||||
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
|
||||
NamedValueInfo result = namedValueInfoCache.get(parameter);
|
||||
if (result == null) {
|
||||
NamedValueInfo info = createNamedValueInfo(parameter);
|
||||
String name = info.name;
|
||||
if (name.length() == 0) {
|
||||
name = parameter.getParameterName();
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("No parameter name specified for argument of type [" +
|
||||
parameter.getParameterType().getName() +
|
||||
"], and no parameter name information found in class file either.");
|
||||
}
|
||||
}
|
||||
boolean required = info.required;
|
||||
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
|
||||
|
||||
result = new NamedValueInfo(name, required, defaultValue);
|
||||
namedValueInfoCache.put(parameter, result);
|
||||
NamedValueInfo namedValueInfo = namedValueInfoCache.get(parameter);
|
||||
if (namedValueInfo == null) {
|
||||
namedValueInfo = createNamedValueInfo(parameter);
|
||||
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
|
||||
namedValueInfoCache.put(parameter, namedValueInfo);
|
||||
}
|
||||
return result;
|
||||
return namedValueInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NamedValueInfo} object for the given method parameter.
|
||||
*
|
||||
* <p>Implementations typically retrieve the method annotation by means of {@link
|
||||
* MethodParameter#getParameterAnnotation(Class)}.
|
||||
*
|
||||
* Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
|
||||
* retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}.
|
||||
*
|
||||
* @param parameter the method parameter
|
||||
* @return the named value information
|
||||
*/
|
||||
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolves the given parameter into a method argument.
|
||||
* Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values.
|
||||
*/
|
||||
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
|
||||
String name = info.name;
|
||||
if (info.name.length() == 0) {
|
||||
name = parameter.getParameterName();
|
||||
Assert.notNull(name, "Name for argument type [" + parameter.getParameterType().getName()
|
||||
+ "] not available, and parameter name information not found in class file either.");
|
||||
}
|
||||
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
|
||||
return new NamedValueInfo(name, info.required, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given parameter type and value name into an argument value.
|
||||
* @param name the name of the value being resolved
|
||||
* @param parameter the method parameter to resolve to an argument value
|
||||
* @param request the current request
|
||||
*
|
||||
* @param webRequest the current web request, allowing access to the native request as well
|
||||
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to the
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)} method of this interface, which must have
|
||||
* returned {@code true}.
|
||||
* @param name the name
|
||||
* @return the resolved argument. May be {@code null}.
|
||||
* @return the resolved argument. May be {@code null}
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
protected abstract Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String name) throws Exception;
|
||||
protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
|
||||
throws Exception;
|
||||
|
||||
private Object resolveDefaultValue(String value) {
|
||||
/**
|
||||
* Resolves the given default value into an argument value.
|
||||
*/
|
||||
private Object resolveDefaultValue(String defaultValue) {
|
||||
if (beanFactory == null) {
|
||||
return value;
|
||||
return defaultValue;
|
||||
}
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(defaultValue);
|
||||
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
|
||||
if (exprResolver == null) {
|
||||
return value;
|
||||
return defaultValue;
|
||||
}
|
||||
return exprResolver.evaluate(placeholdersResolved, expressionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a named value is required, but
|
||||
* {@link #resolveNamedValueArgument(NativeWebRequest, MethodParameter, String)} returned {@code null}
|
||||
* and there is no default value set.
|
||||
*
|
||||
* <p>Concrete subclasses typically throw an exception in this scenario.
|
||||
*
|
||||
* @param name the name
|
||||
* Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
|
||||
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
|
||||
* @param name the name for the value
|
||||
* @param parameter the method parameter
|
||||
*/
|
||||
protected abstract void handleMissingValue(String name, MethodParameter parameter) throws ServletException;
|
||||
@@ -189,7 +200,5 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
this.required = required;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,32 +21,48 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Adapts a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver} contract.
|
||||
* An abstract base class adapting a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver}
|
||||
* contract. Provided for backwards compatibility, some important considerations are listed below.
|
||||
*
|
||||
* <p>The method {@link #supportsParameter(MethodParameter)} is implemented by trying to resolve the value through
|
||||
* the {@link WebArgumentResolver} and verifying the resulting value is not {@link WebArgumentResolver#UNRESOLVED}.
|
||||
* Exceptions resulting from that are absorbed and ignored since the adapter can't be sure if this is the resolver
|
||||
* that supports the method parameter or not. To avoid this limitation change the {@link WebArgumentResolver} to
|
||||
* implement the {@link HandlerMethodArgumentResolver} contract instead.
|
||||
*
|
||||
* <p>Another potentially useful advantage of {@link HandlerMethodArgumentResolver} is that it provides access to
|
||||
* model attributes through the {@link ModelAndViewContainer} as well as access to a {@link WebDataBinderFactory}
|
||||
* for when type conversion through a {@link WebDataBinder} is needed.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
|
||||
public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final WebArgumentResolver adaptee;
|
||||
|
||||
public WebArgumentResolverAdapter(WebArgumentResolver adaptee) {
|
||||
/**
|
||||
* Create a {@link AbstractWebArgumentResolverAdapter} with the {@link WebArgumentResolver} instance to delegate to.
|
||||
*/
|
||||
public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
|
||||
Assert.notNull(adaptee, "'adaptee' must not be null");
|
||||
this.adaptee = adaptee;
|
||||
}
|
||||
|
||||
/**
|
||||
* See the class-level documentation for an important consideration about exceptions arising in this method.
|
||||
*/
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
try {
|
||||
NativeWebRequest webRequest = getWebRequest();
|
||||
@@ -64,16 +80,17 @@ public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected NativeWebRequest getWebRequest() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
return (requestAttributes instanceof NativeWebRequest) ? (NativeWebRequest) requestAttributes : null;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Provide access to a {@link NativeWebRequest}.
|
||||
*/
|
||||
protected abstract NativeWebRequest getWebRequest();
|
||||
|
||||
/**
|
||||
* Resolves the argument value by delegating to the {@link WebArgumentResolver} instance.
|
||||
* @exception IllegalStateException if the resolved value is {@link WebArgumentResolver#UNRESOLVED} or if the
|
||||
* return value type cannot be assigned to the method parameter type.
|
||||
*/
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -88,4 +105,4 @@ public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,18 +23,19 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* An implementation of {@link HandlerMethodArgumentResolver} that resolves {@link Errors} method parameters.
|
||||
* Such parameters must be preceded by {@link ModelAttribute} parameters as described in {@link RequestMapping}.
|
||||
* Resolves method arguments of type {@link Errors} and {@link BindingResult}.
|
||||
*
|
||||
* <p>This argument should appear after a model attribute argument in the signature of the handler method.
|
||||
* It is resolved by accessing the last attribute in the model expecting that to be a {@link BindingResult}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@@ -43,10 +44,6 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
|
||||
return Errors.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -67,5 +64,4 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
|
||||
private boolean isBindingResultKey(String key) {
|
||||
return key.startsWith(BindingResult.MODEL_KEY_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,18 +21,28 @@ import javax.servlet.ServletException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated
|
||||
* with {@link Value @Value}.
|
||||
* Resolves method arguments annotated with @{@link Value}.
|
||||
*
|
||||
* <p>An @{@link Value} is a named value that does not have a name but gets resolved from a default value string
|
||||
* that may contain ${...} placeholder or Spring Expression Language #{...} expressions. See the base class
|
||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
||||
*
|
||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved argument values that don't yet match
|
||||
* the method parameter type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
*/
|
||||
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
@@ -48,16 +58,15 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, MethodParameter parameter, String name)
|
||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
// Only interested in default value resolution
|
||||
// There is no name to be resolved
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
|
||||
// Should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException("Did not expect to handle a missing value: an @Value is never required");
|
||||
}
|
||||
|
||||
private static class ExpressionValueNamedValueInfo extends NamedValueInfo {
|
||||
@@ -65,6 +74,5 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
|
||||
private ExpressionValueNamedValueInfo(Value annotation) {
|
||||
super("@Value", false, annotation.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
@@ -34,24 +35,30 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves model attribute method parameters.
|
||||
* Resolves method arguments annotated with @{@link ModelAttribute}. Or if created in default resolution mode,
|
||||
* resolves any non-simple type argument even without an @{@link ModelAttribute}. See the constructor for details.
|
||||
*
|
||||
* <p>A model attribute argument value is obtained from the model or is created using its default constructor.
|
||||
* Data binding and optionally validation is then applied through a {@link WebDataBinder} instance. Validation is
|
||||
* invoked optionally when the argument is annotated with an {@code @Valid}.
|
||||
*
|
||||
* <p>Also handles return values from methods annotated with an @{@link ModelAttribute}. The return value is
|
||||
* added to the {@link ModelAndViewContainer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAttributeMethodProcessor
|
||||
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
private final boolean resolveArgumentsWithoutAnnotations;
|
||||
private final boolean useDefaultResolution;
|
||||
|
||||
/**
|
||||
* Creates a {@link ModelAttributeMethodProcessor} instance.
|
||||
* @param resolveArgumentsWithoutAnnotations enable default resolution mode in which arguments without
|
||||
* annotations that aren't simple types (see {@link BeanUtils#isSimpleProperty(Class)})
|
||||
* are also treated as model attributes with a default name based on the model attribute type.
|
||||
* @param useDefaultResolution in default resolution mode a method argument that isn't a simple type, as
|
||||
* defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a model attribute even if it doesn't
|
||||
* have an @{@link ModelAttribute} annotation with its name derived from the model attribute type.
|
||||
*/
|
||||
public ModelAttributeMethodProcessor(boolean resolveArgumentsWithoutAnnotations) {
|
||||
this.resolveArgumentsWithoutAnnotations = resolveArgumentsWithoutAnnotations;
|
||||
public ModelAttributeMethodProcessor(boolean useDefaultResolution) {
|
||||
this.useDefaultResolution = useDefaultResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +69,7 @@ public class ModelAttributeMethodProcessor
|
||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||
return true;
|
||||
}
|
||||
else if (this.resolveArgumentsWithoutAnnotations) {
|
||||
else if (this.useDefaultResolution) {
|
||||
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
|
||||
}
|
||||
else {
|
||||
@@ -70,15 +77,13 @@ public class ModelAttributeMethodProcessor
|
||||
}
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the argument to a model attribute creating a {@link WebDataBinder} and invoking data binding on it.
|
||||
* The model attribute is obtained from the model first or otherwise created via direct instantiation.
|
||||
* @throws Exception if data binder initialization fails or if data binding results in errors and the next
|
||||
* method parameter is not of type {@link Errors}.
|
||||
* Resolves the argument to a model attribute looking up the attribute in the model or instantiating it using its
|
||||
* default constructor. Data binding and optionally validation is then applied through a {@link WebDataBinder}
|
||||
* instance. Validation is invoked optionally when the method parameter is annotated with an {@code @Valid}.
|
||||
*
|
||||
* @throws Exception if a {@link WebDataBinder} could not be created or if data binding and validation result in
|
||||
* an error and the next method parameter is not of type {@link Errors} or {@link BindingResult}.
|
||||
*/
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
@@ -164,5 +169,4 @@ public class ModelAttributeMethodProcessor
|
||||
mavContainer.addAttribute(name, returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,12 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves {@link Model} and {@link Map} method parameters.
|
||||
* Resolves {@link Map} and {@link Model} method arguments.
|
||||
*
|
||||
* <p>Handles {@link Model} return values adding their attributes to the {@link ModelAndViewContainer}.
|
||||
* Handles {@link Map} return values in the same way as long as the method does not have an @{@link ModelAttribute}.
|
||||
* If the method does have an @{@link ModelAttribute}, it is assumed the returned {@link Map} is a model attribute
|
||||
* and not a model.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -40,10 +45,6 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
return Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -78,5 +79,4 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,17 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
* {@link RequestHeader @RequestHeader}.
|
||||
* Resolves {@link Map} method arguments annotated with an @{@link RequestHeader}.
|
||||
* See {@link RequestHeaderMethodArgumentResolver} for individual header values with an @{@link RequestHeader}.
|
||||
*
|
||||
* <p>The created {@link Map} contains all request header name/value pairs. If the method parameter type
|
||||
* is {@link MultiValueMap} instead, the created map contains all request headers and all their values in case
|
||||
* request headers have multiple values.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
* @see RequestHeaderMethodArgumentResolver
|
||||
*/
|
||||
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@@ -43,10 +50,6 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
||||
&& Map.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -79,4 +82,4 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,18 +20,31 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link RequestHeader @RequestHeader}.
|
||||
* Resolves method arguments annotated with @{@link RequestHeader} with the exception of {@link Map} arguments.
|
||||
* See {@link RequestHeaderMapMethodArgumentResolver} for {@link Map} arguments annotated with @{@link RequestHeader}.
|
||||
*
|
||||
* <p>An @{@link RequestHeader} is a named value that gets resolved from a request header. It has a required flag
|
||||
* and a default value to fall back on when the request header does not exist. See the base class
|
||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
||||
*
|
||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved request header values that
|
||||
* don't yet match the method parameter type.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
*/
|
||||
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
@@ -48,10 +61,8 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String headerName) throws Exception {
|
||||
String[] headerValues = webRequest.getHeaderValues(headerName);
|
||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||
String[] headerValues = request.getHeaderValues(name);
|
||||
if (headerValues != null) {
|
||||
return (headerValues.length == 1 ? headerValues[0] : headerValues);
|
||||
}
|
||||
@@ -62,8 +73,9 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String headerName, MethodParameter parameter) {
|
||||
String paramTypeName = parameter.getParameterType().getName();
|
||||
throw new IllegalStateException(
|
||||
"Missing header '" + headerName + "' of type [" + parameter.getParameterType().getName() + "]");
|
||||
"Missing header '" + headerName + "' for method parameter type [" + paramTypeName + "]");
|
||||
}
|
||||
|
||||
private static class RequestHeaderNamedValueInfo extends NamedValueInfo {
|
||||
@@ -72,5 +84,4 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,18 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
* {@link RequestParam @RequestParam}.
|
||||
* Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not
|
||||
* specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
|
||||
* method arguments with a request parameter name.
|
||||
*
|
||||
* <p>The created {@link Map} contains all request parameter name/value pairs. If the method parameter type
|
||||
* is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for
|
||||
* cases where request parameters have multiple values.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
* @see RequestParamMethodArgumentResolver
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@@ -47,10 +55,6 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
@@ -77,4 +81,4 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.method.annotation.support;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -24,38 +25,51 @@ import javax.servlet.ServletException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link RequestParam @RequestParam}.
|
||||
*
|
||||
* Resolves method arguments annotated with @{@link RequestParam}.
|
||||
*
|
||||
* <p>If the method parameter type is {@link Map}, the request parameter name is resolved and then converted
|
||||
* to a {@link Map} via type conversion assuming a suitable {@link PropertyEditor} or {@link Converter} is
|
||||
* registered. Alternatively, see {@link RequestParamMapMethodArgumentResolver} for access to all request
|
||||
* parameters in a {@link Map}.
|
||||
*
|
||||
* <p>If this class is created with default resolution mode on, simple types not annotated
|
||||
* with @{@link RequestParam} are also treated as request parameters with the parameter name based
|
||||
* on the method argument name. See the class constructor for more details.
|
||||
*
|
||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved request header values that
|
||||
* don't yet match the method parameter type.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
* @see RequestParamMapMethodArgumentResolver
|
||||
*/
|
||||
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
private final boolean resolveParamsWithoutAnnotations;
|
||||
private final boolean useDefaultResolution;
|
||||
|
||||
/**
|
||||
* Creates a {@link RequestParamMethodArgumentResolver} instance.
|
||||
*
|
||||
* @param beanFactory the bean factory to use for resolving default value expressions
|
||||
* @param resolveParamsWithoutAnnotations enable default resolution mode in which parameters without
|
||||
* annotations that are simple types (see {@link BeanUtils#isSimpleProperty(Class)})
|
||||
* are also treated as model attributes with a default name based on the method argument name.
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
* @param useDefaultResolution in default resolution mode a method argument that is a simple type, as
|
||||
* defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a request parameter even if it doesn't have
|
||||
* an @{@link RequestParam} annotation, the request parameter name is derived from the method parameter name.
|
||||
*/
|
||||
public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
boolean resolveParamsWithoutAnnotations) {
|
||||
boolean useDefaultResolution) {
|
||||
super(beanFactory);
|
||||
this.resolveParamsWithoutAnnotations = resolveParamsWithoutAnnotations;
|
||||
this.useDefaultResolution = useDefaultResolution;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
@@ -67,7 +81,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (this.resolveParamsWithoutAnnotations && !parameter.hasParameterAnnotations()) {
|
||||
else if (this.useDefaultResolution) {
|
||||
return BeanUtils.isSimpleProperty(paramType);
|
||||
}
|
||||
else {
|
||||
@@ -84,18 +98,16 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String paramName) throws Exception {
|
||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
|
||||
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
|
||||
if (multipartRequest != null) {
|
||||
List<MultipartFile> files = multipartRequest.getFiles(paramName);
|
||||
List<MultipartFile> files = multipartRequest.getFiles(name);
|
||||
if (!files.isEmpty()) {
|
||||
return (files.size() == 1 ? files.get(0) : files);
|
||||
}
|
||||
}
|
||||
|
||||
String[] paramValues = webRequest.getParameterValues(paramName);
|
||||
String[] paramValues = webRequest.getParameterValues(name);
|
||||
if (paramValues != null) {
|
||||
return paramValues.length == 1 ? paramValues[0] : paramValues;
|
||||
}
|
||||
@@ -119,5 +131,4 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user