POLISH ARGUMENT RESOLVERS AND RETURN VALUE HANDLERS.
This commit is contained in:
parent
fe7e2a7f54
commit
6bc4ea058c
|
|
@ -33,11 +33,10 @@ import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
|
||||||
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.HandlerMethod;
|
||||||
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
|
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
|
||||||
|
import org.springframework.web.method.annotation.support.MapMethodProcessor;
|
||||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
|
@ -48,29 +47,23 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
|
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.support.ViewNameMethodReturnValueHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AbstractHandlerMethodExceptionResolver} that supports using {@link ExceptionHandler}-annotated methods
|
* An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions
|
||||||
* to resolve exceptions.
|
* through {@code @ExceptionHandler} methods.
|
||||||
*
|
|
||||||
* <p>{@link ExceptionHandlerMethodResolver} is a key contributing class that stores method-to-exception mappings extracted
|
|
||||||
* from {@link ExceptionHandler} annotations or from the list of method arguments on the exception-handling method.
|
|
||||||
* {@link ExceptionHandlerMethodResolver} assists with actually locating a method for a thrown exception.
|
|
||||||
*
|
|
||||||
* <p>Once located the invocation of the exception-handling method is done using much of the same classes
|
|
||||||
* used for {@link RequestMapping} methods, which is described under {@link RequestMappingHandlerAdapter}.
|
|
||||||
*
|
|
||||||
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values for
|
|
||||||
* exception-handling methods.
|
|
||||||
*
|
*
|
||||||
|
* <p>Support for custom argument and return value types can be added via
|
||||||
|
* {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
|
||||||
|
* Or alternatively to re-configure all argument and return value types use
|
||||||
|
* {@link #setArgumentResolvers} and {@link #setReturnValueHandlers(List)}.
|
||||||
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
|
|
@ -91,66 +84,96 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||||
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
|
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of {@link ExceptionHandlerExceptionResolver}.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
public ExceptionHandlerExceptionResolver() {
|
public ExceptionHandlerExceptionResolver() {
|
||||||
|
|
||||||
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
|
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
|
||||||
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
|
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
|
||||||
|
|
||||||
messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||||
messageConverters.add(new ByteArrayHttpMessageConverter());
|
this.messageConverters.add(new ByteArrayHttpMessageConverter());
|
||||||
messageConverters.add(stringHttpMessageConverter);
|
this.messageConverters.add(stringHttpMessageConverter);
|
||||||
messageConverters.add(new SourceHttpMessageConverter<Source>());
|
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
|
||||||
messageConverters.add(new XmlAwareFormHttpMessageConverter());
|
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set one or more custom argument resolvers to use with {@link ExceptionHandler} methods. Custom argument resolvers
|
* Provide resolvers for custom argument types. Custom resolvers are ordered
|
||||||
* are given a chance to resolve argument values ahead of the standard argument resolvers registered by default.
|
* after built-in ones. To override the built-in support for argument
|
||||||
* <p>An existing {@link WebArgumentResolver} can either adapted with {@link ServletWebArgumentResolverAdapter}
|
* resolution use {@link #setArgumentResolvers} instead.
|
||||||
* or preferably converted to a {@link HandlerMethodArgumentResolver} instead.
|
|
||||||
*/
|
*/
|
||||||
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||||
this.customArgumentResolvers= argumentResolvers;
|
this.customArgumentResolvers= argumentResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the custom argument resolvers, or {@code null}.
|
||||||
|
*/
|
||||||
|
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
|
||||||
|
return this.customArgumentResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the argument resolvers to use with {@link ExceptionHandler} methods.
|
* Configure the complete list of supported argument types thus overriding
|
||||||
* This is an optional property providing full control over all argument resolvers in contrast to
|
* the resolvers that would otherwise be configured by default.
|
||||||
* {@link #setCustomArgumentResolvers(List)}, which does not override default registrations.
|
|
||||||
* @param argumentResolvers argument resolvers for {@link ExceptionHandler} methods
|
|
||||||
*/
|
*/
|
||||||
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||||
if (argumentResolvers != null) {
|
if (argumentResolvers == null) {
|
||||||
|
this.argumentResolvers = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
||||||
this.argumentResolvers.addResolvers(argumentResolvers);
|
this.argumentResolvers.addResolvers(argumentResolvers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured argument resolvers, or possibly {@code null} if
|
||||||
|
* not initialized yet via {@link #afterPropertiesSet()}.
|
||||||
|
*/
|
||||||
|
public HandlerMethodArgumentResolverComposite getArgumentResolvers() {
|
||||||
|
return this.argumentResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set custom return value handlers to use to handle the return values of {@link ExceptionHandler} methods.
|
* Provide handlers for custom return value types. Custom handlers are
|
||||||
* Custom return value handlers are given a chance to handle a return value before the standard
|
* ordered after built-in ones. To override the built-in support for
|
||||||
* return value handlers registered by default.
|
* return value handling use {@link #setReturnValueHandlers}.
|
||||||
* @param returnValueHandlers custom return value handlers for {@link ExceptionHandler} methods
|
|
||||||
*/
|
*/
|
||||||
public void setCustomReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
public void setCustomReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||||
this.customReturnValueHandlers = returnValueHandlers;
|
this.customReturnValueHandlers = returnValueHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link HandlerMethodReturnValueHandler}s to use to use with {@link ExceptionHandler} methods.
|
* Return the custom return value handlers, or {@code null}.
|
||||||
* This is an optional property providing full control over all return value handlers in contrast to
|
*/
|
||||||
* {@link #setCustomReturnValueHandlers(List)}, which does not override default registrations.
|
public List<HandlerMethodReturnValueHandler> getCustomReturnValueHandlers() {
|
||||||
* @param returnValueHandlers the return value handlers for {@link ExceptionHandler} methods
|
return this.customReturnValueHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the complete list of supported return value types thus
|
||||||
|
* overriding handlers that would otherwise be configured by default.
|
||||||
*/
|
*/
|
||||||
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||||
if (returnValueHandlers != null) {
|
if (returnValueHandlers == null) {
|
||||||
|
this.returnValueHandlers = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
|
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
|
||||||
this.returnValueHandlers.addHandlers(returnValueHandlers);
|
this.returnValueHandlers.addHandlers(returnValueHandlers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured handlers, or possibly {@code null} if not
|
||||||
|
* initialized yet via {@link #afterPropertiesSet()}.
|
||||||
|
*/
|
||||||
|
public HandlerMethodReturnValueHandlerComposite getReturnValueHandlers() {
|
||||||
|
return this.returnValueHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the message body converters to use.
|
* Set the message body converters to use.
|
||||||
* <p>These converters are used to convert from and to HTTP requests and responses.
|
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||||
|
|
@ -158,44 +181,72 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||||
this.messageConverters = messageConverters;
|
this.messageConverters = messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured message body converters.
|
||||||
|
*/
|
||||||
|
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||||
|
return messageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
if (argumentResolvers == null) {
|
if (this.argumentResolvers == null) {
|
||||||
argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
|
||||||
argumentResolvers.addResolvers(customArgumentResolvers);
|
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
|
||||||
argumentResolvers.addResolvers(getDefaultArgumentResolvers());
|
|
||||||
}
|
}
|
||||||
if (returnValueHandlers == null) {
|
if (this.returnValueHandlers == null) {
|
||||||
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
|
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
|
||||||
returnValueHandlers.addHandlers(customReturnValueHandlers);
|
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
|
||||||
returnValueHandlers.addHandlers(getDefaultReturnValueHandlers(messageConverters));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
|
/**
|
||||||
|
* Return the list of argument resolvers to use including built-in resolvers
|
||||||
|
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
|
||||||
|
*/
|
||||||
|
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
|
||||||
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
||||||
|
|
||||||
|
// Type-based argument resolution
|
||||||
resolvers.add(new ServletRequestMethodArgumentResolver());
|
resolvers.add(new ServletRequestMethodArgumentResolver());
|
||||||
resolvers.add(new ServletResponseMethodArgumentResolver());
|
resolvers.add(new ServletResponseMethodArgumentResolver());
|
||||||
|
|
||||||
|
// Custom arguments
|
||||||
|
if (getCustomArgumentResolvers() != null) {
|
||||||
|
resolvers.addAll(getCustomArgumentResolvers());
|
||||||
|
}
|
||||||
|
|
||||||
return resolvers;
|
return resolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers(
|
/**
|
||||||
List<HttpMessageConverter<?>> messageConverters) {
|
* Return the list of return value handlers to use including built-in and
|
||||||
|
* custom handlers provided via {@link #setReturnValueHandlers}.
|
||||||
|
*/
|
||||||
|
protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
|
||||||
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
|
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
|
||||||
|
|
||||||
// Annotation-based handlers
|
// Single-purpose return value types
|
||||||
handlers.add(new RequestResponseBodyMethodProcessor(messageConverters));
|
|
||||||
handlers.add(new ModelAttributeMethodProcessor(false));
|
|
||||||
|
|
||||||
// Type-based handlers
|
|
||||||
handlers.add(new ModelAndViewMethodReturnValueHandler());
|
handlers.add(new ModelAndViewMethodReturnValueHandler());
|
||||||
handlers.add(new ModelMethodProcessor());
|
handlers.add(new ModelMethodProcessor());
|
||||||
handlers.add(new ViewMethodReturnValueHandler());
|
handlers.add(new ViewMethodReturnValueHandler());
|
||||||
handlers.add(new HttpEntityMethodProcessor(messageConverters));
|
handlers.add(new HttpEntityMethodProcessor(getMessageConverters()));
|
||||||
|
|
||||||
// Default handler
|
// Annotation-based return value types
|
||||||
handlers.add(new DefaultMethodReturnValueHandler());
|
handlers.add(new ModelAttributeMethodProcessor(false));
|
||||||
|
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
|
||||||
|
|
||||||
|
// Multi-purpose return value types
|
||||||
|
handlers.add(new ViewNameMethodReturnValueHandler());
|
||||||
|
handlers.add(new MapMethodProcessor());
|
||||||
|
|
||||||
|
// Custom return value types
|
||||||
|
if (getCustomReturnValueHandlers() != null) {
|
||||||
|
handlers.addAll(getCustomReturnValueHandlers());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch-all
|
||||||
|
handlers.add(new ModelAttributeMethodProcessor(true));
|
||||||
|
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
@ -77,9 +78,9 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
|
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewResolverMethodReturnValueHandler;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver;
|
import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.RedirectAttributesMethodArgumentResolver;
|
import org.springframework.web.servlet.mvc.method.annotation.support.RedirectAttributesMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartMethodArgumentResolver;
|
import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartMethodArgumentResolver;
|
||||||
|
|
@ -261,9 +262,18 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide custom {@link ModelAndViewResolver}s. This is available for
|
* Provide custom {@link ModelAndViewResolver}s.
|
||||||
* backwards compatibility. However it is recommended to use
|
* <p><strong>Note:</strong> This method is available for backwards
|
||||||
* {@link HandlerMethodReturnValueHandler}s instead.
|
* compatibility only. However, it is recommended to re-write a
|
||||||
|
* {@code ModelAndViewResolver} as {@link HandlerMethodReturnValueHandler}.
|
||||||
|
* An adapter between the two interfaces is not possible since the
|
||||||
|
* {@link HandlerMethodReturnValueHandler#supportsReturnType} method
|
||||||
|
* cannot be implemented. Hence {@code ModelAndViewResolver}s are limited
|
||||||
|
* to always being invoked at the end after all other return value
|
||||||
|
* handlers have been given a chance.
|
||||||
|
* <p>A {@code HandlerMethodReturnValueHandler} provides better access to
|
||||||
|
* the return type and controller method information and can be ordered
|
||||||
|
* freely relative to other return value handlers.
|
||||||
*/
|
*/
|
||||||
public void setModelAndViewResolvers(List<ModelAndViewResolver> modelAndViewResolvers) {
|
public void setModelAndViewResolvers(List<ModelAndViewResolver> modelAndViewResolvers) {
|
||||||
this.modelAndViewResolvers = modelAndViewResolvers;
|
this.modelAndViewResolvers = modelAndViewResolvers;
|
||||||
|
|
@ -507,7 +517,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch-all
|
// Catch-all
|
||||||
handlers.add(new DefaultMethodReturnValueHandler(getModelAndViewResolvers()));
|
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
|
||||||
|
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handlers.add(new ModelAttributeMethodProcessor(true));
|
||||||
|
}
|
||||||
|
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import java.util.Map;
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
|
||||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
|
@ -31,8 +30,8 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link ServletRequestDataBinder} instance and extends it with the ability to include
|
* Creates a WebDataBinder of type {@link ServletRequestDataBinder} that can
|
||||||
* URI template variables in the values used for data binding purposes.
|
* also use URI template variables values for data binding purposes.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -41,41 +40,45 @@ public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
* @param binderMethods {@link InitBinder} methods to initialize new data binder instances with
|
* @param binderMethods one or more {@code @InitBinder} methods
|
||||||
* @param iitializer a global initializer to initialize new data binder instances with
|
* @param initializer provides global data binder initialization
|
||||||
*/
|
*/
|
||||||
public ServletRequestDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer iitializer) {
|
public ServletRequestDataBinderFactory(List<InvocableHandlerMethod> binderMethods,
|
||||||
super(binderMethods, iitializer);
|
WebBindingInitializer initializer) {
|
||||||
|
super(binderMethods, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link ServletRequestDataBinder} instance extended with the ability to add
|
* Create a WebDataBinder of type {@link ServletRequestDataBinder} that can
|
||||||
* URI template variables the values used for data binding.
|
* also use URI template variables values for data binding purposes.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected WebDataBinder createBinderInstance(Object target, String objectName, final NativeWebRequest request) {
|
protected WebDataBinder createBinderInstance(Object target, String objectName, final NativeWebRequest request) {
|
||||||
return new ServletRequestDataBinder(target, objectName) {
|
return new ServletRequestDataBinder(target, objectName) {
|
||||||
|
@Override
|
||||||
protected void doBind(MutablePropertyValues mpvs) {
|
protected void doBind(MutablePropertyValues mpvs) {
|
||||||
addUriTemplateVars(mpvs, request);
|
mergeUriTemplateVariables(mpvs, request);
|
||||||
super.doBind(mpvs);
|
super.doBind(mpvs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds URI template variables to the the property values used for data binding.
|
* Merge URI variable values into the given PropertyValues.
|
||||||
* @param mpvs the PropertyValues to use for data binding
|
* @param mpvs the PropertyValues to add to
|
||||||
|
* @param request the current request
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected final void addUriTemplateVars(MutablePropertyValues mpvs, NativeWebRequest request) {
|
protected final void mergeUriTemplateVariables(MutablePropertyValues mpvs, NativeWebRequest request) {
|
||||||
|
|
||||||
Map<String, String> uriTemplateVars =
|
Map<String, String> uriTemplateVars =
|
||||||
(Map<String, String>) request.getAttribute(
|
(Map<String, String>) request.getAttribute(
|
||||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||||
|
|
||||||
if (uriTemplateVars != null){
|
if (uriTemplateVars != null){
|
||||||
for (String name : uriTemplateVars.keySet()) {
|
for (String variableName : uriTemplateVars.keySet()) {
|
||||||
if (!mpvs.contains(name)) {
|
if (!mpvs.contains(variableName)) {
|
||||||
mpvs.addPropertyValue(name, uriTemplateVars.get(name));
|
mpvs.addPropertyValue(variableName, uriTemplateVars.get(variableName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2011 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.ui.ExtendedModelMap;
|
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
|
||||||
import org.springframework.web.method.annotation.ModelFactory;
|
|
||||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to handle return value types not recognized by any other {@link HandlerMethodReturnValueHandler}.
|
|
||||||
* Intended to be used as the last of a list of registered handlers as {@link #supportsReturnType(MethodParameter)}
|
|
||||||
* always returns {@code true}.
|
|
||||||
* <p>Handling takes place in the following order:
|
|
||||||
* <ul>
|
|
||||||
* <li>Iterate over the list of {@link ModelAndViewResolver}s provided to the constructor of this class looking
|
|
||||||
* for a return value that isn't {@link ModelAndViewResolver#UNRESOLVED}.
|
|
||||||
* <li>If the return value is not a simple type it is treated as a single model attribute to be added to the model
|
|
||||||
* with a name derived from its type.
|
|
||||||
* </ul>
|
|
||||||
* <p>Note that {@link ModelAndViewResolver} is supported for backwards compatibility. Since the only way to check
|
|
||||||
* if it supports a return value type is to try to resolve the return value, a {@link ModelAndViewResolver} can
|
|
||||||
* only be invoked from here after no other {@link HandlerMethodReturnValueHandler} has recognized the return
|
|
||||||
* value. To avoid this limitation change the {@link ModelAndViewResolver} to implement
|
|
||||||
* {@link HandlerMethodReturnValueHandler} instead.
|
|
||||||
*
|
|
||||||
* @author Rossen Stoyanchev
|
|
||||||
* @since 3.1
|
|
||||||
*/
|
|
||||||
public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
|
||||||
|
|
||||||
private final List<ModelAndViewResolver> mavResolvers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link DefaultMethodReturnValueHandler} instance without {@link ModelAndViewResolver}s.
|
|
||||||
*/
|
|
||||||
public DefaultMethodReturnValueHandler() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link DefaultMethodReturnValueHandler} with a list of {@link ModelAndViewResolver}s.
|
|
||||||
*/
|
|
||||||
public DefaultMethodReturnValueHandler(List<ModelAndViewResolver> mavResolvers) {
|
|
||||||
this.mavResolvers = mavResolvers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean supportsReturnType(MethodParameter returnType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleReturnValue(Object returnValue,
|
|
||||||
MethodParameter returnType,
|
|
||||||
ModelAndViewContainer mavContainer,
|
|
||||||
NativeWebRequest request) throws Exception {
|
|
||||||
|
|
||||||
if (mavResolvers != null) {
|
|
||||||
for (ModelAndViewResolver resolver : mavResolvers) {
|
|
||||||
Class<?> handlerType = returnType.getDeclaringClass();
|
|
||||||
Method method = returnType.getMethod();
|
|
||||||
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
|
|
||||||
ModelAndView mav = resolver.resolveModelAndView(method, handlerType, returnValue, model, request);
|
|
||||||
if (mav != ModelAndViewResolver.UNRESOLVED) {
|
|
||||||
mavContainer.addAllAttributes(mav.getModel());
|
|
||||||
mavContainer.setViewName(mav.getViewName());
|
|
||||||
if (!mav.isReference()) {
|
|
||||||
mavContainer.setView(mav.getView());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnValue == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
|
|
||||||
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
|
|
||||||
mavContainer.addAttribute(name, returnValue);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// should not happen..
|
|
||||||
Method method = returnType.getMethod();
|
|
||||||
String returnTypeName = returnType.getParameterType().getName();
|
|
||||||
throw new UnsupportedOperationException("Unknown return type: " + returnTypeName + " in method: " + method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.ui.ExtendedModelMap;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This return value handler is intended to be ordered after all others as it
|
||||||
|
* attempts to handle _any_ return value type (i.e. returns {@code true} for
|
||||||
|
* all return types).
|
||||||
|
*
|
||||||
|
* <p>The return value is handled either with a {@link ModelAndViewResolver}
|
||||||
|
* or otherwise by regarding it as a model attribute if it is a non-simple
|
||||||
|
* type. If neither of these succeeds (essentially simple type other than
|
||||||
|
* String), {@link UnsupportedOperationException} is raised.
|
||||||
|
*
|
||||||
|
* <p><strong>Note:</strong> This class is primarily needed to support
|
||||||
|
* {@link ModelAndViewResolver}, which unfortunately cannot be properly
|
||||||
|
* adapted to the {@link HandlerMethodReturnValueHandler} contract since the
|
||||||
|
* {@link HandlerMethodReturnValueHandler#supportsReturnType} method
|
||||||
|
* cannot be implemented. Hence {@code ModelAndViewResolver}s are limited
|
||||||
|
* to always being invoked at the end after all other return value
|
||||||
|
* handlers have been given a chance. It is recommended to re-implement
|
||||||
|
* a {@code ModelAndViewResolver} as {@code HandlerMethodReturnValueHandler},
|
||||||
|
* which also provides better access to the return type and method information.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||||
|
|
||||||
|
private final List<ModelAndViewResolver> mavResolvers;
|
||||||
|
|
||||||
|
private final ModelAttributeMethodProcessor modelAttributeProcessor = new ModelAttributeMethodProcessor(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*/
|
||||||
|
public ModelAndViewResolverMethodReturnValueHandler(List<ModelAndViewResolver> mavResolvers) {
|
||||||
|
this.mavResolvers = mavResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always returns {@code true}. See class-level note.
|
||||||
|
*/
|
||||||
|
public boolean supportsReturnType(MethodParameter returnType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleReturnValue(Object returnValue,
|
||||||
|
MethodParameter returnType,
|
||||||
|
ModelAndViewContainer mavContainer,
|
||||||
|
NativeWebRequest request) throws Exception {
|
||||||
|
|
||||||
|
if (this.mavResolvers != null) {
|
||||||
|
for (ModelAndViewResolver mavResolver : this.mavResolvers) {
|
||||||
|
Class<?> handlerType = returnType.getDeclaringClass();
|
||||||
|
Method method = returnType.getMethod();
|
||||||
|
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
|
||||||
|
ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, request);
|
||||||
|
if (mav != ModelAndViewResolver.UNRESOLVED) {
|
||||||
|
mavContainer.addAllAttributes(mav.getModel());
|
||||||
|
mavContainer.setViewName(mav.getViewName());
|
||||||
|
if (!mav.isReference()) {
|
||||||
|
mavContainer.setView(mav.getView());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No suitable ModelAndViewResolver..
|
||||||
|
|
||||||
|
if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
|
||||||
|
this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new UnsupportedOperationException("Unexpected return type: "
|
||||||
|
+ returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.validation.DataBinder;
|
import org.springframework.validation.DataBinder;
|
||||||
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.method.annotation.support.MapMethodProcessor;
|
||||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
@ -33,8 +34,10 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments of type {@link RedirectAttributes}.
|
* Resolves method arguments of type {@link RedirectAttributes}.
|
||||||
*
|
*
|
||||||
* <p>This resolver must be listed before the {@link ModelMethodProcessor},
|
* <p>This resolver must be listed ahead of {@link ModelMethodProcessor} and
|
||||||
* which resolves {@link Map} and {@link Model} arguments.
|
* {@link MapMethodProcessor}, which support {@link Map} and {@link Model}
|
||||||
|
* arguments both of which are "super" types of {@code RedirectAttributes}
|
||||||
|
* and would also attempt to resolve a {@code RedirectAttributes} argument.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
|
||||||
|
|
@ -48,26 +48,26 @@ import org.springframework.web.util.WebUtils;
|
||||||
/**
|
/**
|
||||||
* Resolves the following method arguments:
|
* Resolves the following method arguments:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Arguments annotated with @{@link RequestPart}.
|
* <li>Annotated with {@code @RequestPart}
|
||||||
* <li>Arguments of type {@link MultipartFile} in conjunction with Spring's
|
* <li>Of type {@link MultipartFile} in conjunction with Spring's
|
||||||
* {@link MultipartResolver} abstraction.
|
* {@link MultipartResolver} abstraction
|
||||||
* <li>Arguments of type {@code javax.servlet.http.Part} in conjunction
|
* <li>Of type {@code javax.servlet.http.Part} in conjunction with
|
||||||
* with Servlet 3.0 multipart requests.
|
* Servlet 3.0 multipart requests
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>When a parameter is annotated with @{@link RequestPart} the content of the
|
* <p>When a parameter is annotated with {@code @RequestPart} the content of the
|
||||||
* part is passed through an {@link HttpMessageConverter} to resolve the method
|
* part is passed through an {@link HttpMessageConverter} to resolve the method
|
||||||
* argument with the 'Content-Type' of the request part in mind. This is
|
* argument with the 'Content-Type' of the request part in mind. This is
|
||||||
* analogous to what @{@link RequestBody} does to resolve an argument based on
|
* analogous to what @{@link RequestBody} does to resolve an argument based on
|
||||||
* the content of a non-multipart request.
|
* the content of a regular request.
|
||||||
*
|
*
|
||||||
* <p>When a parameter is not annotated or the name of the part is not specified,
|
* <p>When a parameter is not annotated or the name of the part is not specified,
|
||||||
* it is derived from the name of the method argument.
|
* it is derived from the name of the method argument.
|
||||||
*
|
*
|
||||||
* <p>Automatic validation can be applied to a @{@link RequestPart} method argument
|
* <p>Automatic validation may be applied if the argument is annotated with
|
||||||
* through the use of {@code @Valid}. In case of validation failure, a
|
* {@code @javax.validation.Valid}. In case of validation failure, a
|
||||||
* {@link MethodArgumentNotValidException} is thrown and handled automatically by
|
* {@link MethodArgumentNotValidException} is raised and a 400 response status
|
||||||
* the {@link DefaultHandlerExceptionResolver}.
|
* code returned if {@link DefaultHandlerExceptionResolver} is configured.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -81,9 +81,9 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
/**
|
/**
|
||||||
* Supports the following:
|
* Supports the following:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>@RequestPart-annotated method arguments.
|
* <li>Annotated with {@code @RequestPart}
|
||||||
* <li>Arguments of type {@link MultipartFile} unless annotated with {@link RequestParam}.
|
* <li>Of type {@link MultipartFile} unless annotated with {@code @RequestParam}.
|
||||||
* <li>Arguments of type {@code javax.servlet.http.Part} unless annotated with {@link RequestParam}.
|
* <li>Of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
|
|
@ -193,15 +193,13 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to validate the given @{@link RequestPart} method argument.
|
* Whether to validate the given {@code @RequestPart} method argument.
|
||||||
* The default implementation return {@code true} if the argument value is not {@code null}
|
* The default implementation looks for {@code @javax.validation.Valid}.
|
||||||
* and the method parameter is annotated with {@code @Valid}.
|
* @param argument the resolved argument value
|
||||||
* @param argumentValue the validation candidate
|
* @param parameter the method argument
|
||||||
* @param parameter the method argument declaring the validation candidate
|
|
||||||
* @return {@code true} if validation should be invoked, {@code false} otherwise.
|
|
||||||
*/
|
*/
|
||||||
protected boolean isValidationApplicable(Object argumentValue, MethodParameter parameter) {
|
protected boolean isValidationApplicable(Object argument, MethodParameter parameter) {
|
||||||
if (argumentValue == null) {
|
if (argument == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,16 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments annotated with @{@link RequestBody} and handles return values from methods
|
* Resolves method arguments annotated with {@code @RequestBody} and handles
|
||||||
* annotated with {@link ResponseBody}.
|
* return values from methods annotated with {@code @ResponseBody} by reading
|
||||||
|
* and writing to the body of the request or response with an
|
||||||
|
* {@link HttpMessageConverter}.
|
||||||
*
|
*
|
||||||
* <p>An @{@link RequestBody} method argument will be validated if annotated with {@code @Valid}.
|
* <p>An {@code @RequestBody} method argument is also validated if it is
|
||||||
* In case of validation failure, a {@link MethodArgumentNotValidException} is thrown and handled
|
* annotated with {@code @javax.validation.Valid}. In case of validation
|
||||||
* automatically in {@link DefaultHandlerExceptionResolver}.
|
* failure, {@link MethodArgumentNotValidException} is raised and results
|
||||||
|
* in a 400 response status code if {@link DefaultHandlerExceptionResolver}
|
||||||
|
* is configured.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
@ -78,13 +82,12 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to validate the given @{@link RequestBody} method argument. The default implementation checks
|
* Whether to validate the given {@code @RequestBody} method argument.
|
||||||
* if the parameter is also annotated with {@code @Valid}.
|
* The default implementation looks for {@code @javax.validation.Valid}.
|
||||||
* @param argumentValue the validation candidate
|
* @param argument the resolved argument value
|
||||||
* @param parameter the method argument declaring the validation candidate
|
* @param parameter the method argument
|
||||||
* @return {@code true} if validation should be invoked, {@code false} otherwise.
|
|
||||||
*/
|
*/
|
||||||
protected boolean isValidationApplicable(Object argumentValue, MethodParameter parameter) {
|
protected boolean isValidationApplicable(Object argument, MethodParameter parameter) {
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||||
for (Annotation annot : annotations) {
|
for (Annotation annot : annotations) {
|
||||||
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ import org.springframework.web.util.UrlPathHelper;
|
||||||
import org.springframework.web.util.WebUtils;
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link AbstractCookieValueMethodArgumentResolver} that resolves the cookie value through the {@link HttpServletRequest}.
|
* An {@link AbstractCookieValueMethodArgumentResolver} that resolves cookie
|
||||||
|
* values from an {@link HttpServletRequest}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
|
||||||
|
|
@ -16,32 +16,29 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||||
|
|
||||||
import java.beans.PropertyEditor;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.validation.DataBinder;
|
import org.springframework.validation.DataBinder;
|
||||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
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.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Servlet-specific {@link ModelAttributeMethodProcessor} variant with the following further benefits:
|
* A Servlet-specific {@link ModelAttributeMethodProcessor} that applies data
|
||||||
* <ul>
|
* binding through a WebDataBinder of type {@link ServletRequestDataBinder}.
|
||||||
* <li>Casts the data binder down to {@link ServletRequestDataBinder} prior to invoking bind on it
|
*
|
||||||
* <li>Attempts to instantiate the model attribute using a path variable and type conversion
|
* <p>Also adds a fall-back strategy to instantiate a model attribute from a
|
||||||
* </ul>
|
* URI template variable combined with type conversion, if the model attribute
|
||||||
* that casts
|
* name matches to a URI template variable name.
|
||||||
* instance to {@link ServletRequestDataBinder} prior to invoking data binding.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -49,39 +46,37 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||||
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
|
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param useDefaultResolution in default resolution mode a method argument that isn't a simple type, as
|
* @param annotationNotRequired if {@code true}, any non-simple type
|
||||||
* defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a model attribute even if it doesn't
|
* argument or return value is regarded as a model attribute even without
|
||||||
* have an @{@link ModelAttribute} annotation with its name derived from the model attribute type.
|
* the presence of a {@code @ModelAttribute} annotation in which case the
|
||||||
|
* attribute name is derived from the model attribute's type.
|
||||||
*/
|
*/
|
||||||
public ServletModelAttributeMethodProcessor(boolean useDefaultResolution) {
|
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
||||||
super(useDefaultResolution);
|
super(annotationNotRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates the model attribute by trying to match the model attribute name to a path variable.
|
* Add a fall-back strategy to instantiate the model attribute from a URI
|
||||||
* If a match is found an attempt is made to convert the String path variable to the expected
|
* template variable and type conversion, assuming the model attribute
|
||||||
* method parameter type through a registered {@link Converter} or {@link PropertyEditor}.
|
* name matches to a URI variable name. If instantiation fails for _any_
|
||||||
* If this fails the call is delegated back to the parent for default constructor instantiation.
|
* reason, the call is delegated to the base class.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected Object createAttribute(String attributeName,
|
protected Object createAttribute(String attributeName,
|
||||||
MethodParameter parameter,
|
MethodParameter parameter,
|
||||||
WebDataBinderFactory binderFactory,
|
WebDataBinderFactory binderFactory,
|
||||||
NativeWebRequest request) throws Exception {
|
NativeWebRequest request) throws Exception {
|
||||||
Map<String, String> uriTemplateVars =
|
|
||||||
(Map<String, String>) request.getAttribute(
|
|
||||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
|
||||||
|
|
||||||
if (uriTemplateVars != null && uriTemplateVars.containsKey(attributeName)) {
|
Map<String, String> uriVariables = getUriTemplateVariables(request);
|
||||||
|
|
||||||
|
if (uriVariables.containsKey(attributeName)) {
|
||||||
try {
|
try {
|
||||||
String var = uriTemplateVars.get(attributeName);
|
|
||||||
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
|
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
|
||||||
return binder.convertIfNecessary(var, parameter.getParameterType());
|
return binder.convertIfNecessary(uriVariables.get(attributeName), parameter.getParameterType());
|
||||||
|
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
logger.info("Model attribute '" + attributeName + "' matches to a URI template variable name. "
|
logger.info("Model attribute name '" + attributeName + "' matches to a URI template variable name "
|
||||||
+ "The URI template variable however couldn't converted to a model attribute instance: "
|
+ "but the variable String value could not be converted into an attribute instance: "
|
||||||
+ exception.getMessage());
|
+ exception.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,9 +84,20 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||||
return super.createAttribute(attributeName, parameter, binderFactory, request);
|
return super.createAttribute(attributeName, parameter, binderFactory, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
|
||||||
|
|
||||||
|
Map<String, String> uriTemplateVars =
|
||||||
|
(Map<String, String>) request.getAttribute(
|
||||||
|
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||||
|
|
||||||
|
return (uriTemplateVars != null) ? uriTemplateVars : Collections.<String, String>emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* <p>This implementation downcasts to {@link ServletRequestDataBinder} before invoking the bind operation.
|
* <p>Downcast {@link WebDataBinder} to {@link ServletRequestDataBinder} before binding.
|
||||||
|
* @see ServletRequestDataBinderFactory
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
|
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ 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.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves response-related method argument values of types:
|
* Resolves response-related method argument values of types:
|
||||||
|
|
@ -53,11 +52,10 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
|
||||||
* <p>Sets the {@link ModelAndViewContainer#setRequestHandled(boolean)} flag to {@code false} to indicate
|
* {@code false} to indicate that the method signature provides access
|
||||||
* that the method signature provides access to the response. If subsequently the underlying method
|
* to the response. If subsequently the underlying method returns
|
||||||
* returns {@code null}, view resolution will be bypassed.
|
* {@code null}, the request is considered directly handled.
|
||||||
* @see ServletInvocableHandlerMethod#invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...)
|
|
||||||
*/
|
*/
|
||||||
public Object resolveArgument(MethodParameter parameter,
|
public Object resolveArgument(MethodParameter parameter,
|
||||||
ModelAndViewContainer mavContainer,
|
ModelAndViewContainer mavContainer,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A test fixture for {@link WebMvcConfigurationSupport}.
|
* A test fixture for {@link WebMvcConfigurationSupport}.
|
||||||
|
|
@ -285,7 +284,7 @@ public class WebMvcConfigurationSupportTests {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||||
returnValueHandlers.add(new DefaultMethodReturnValueHandler());
|
returnValueHandlers.add(new ModelAttributeMethodProcessor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,13 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture with {@link DefaultMethodReturnValueHandler}.
|
* Test fixture with {@link ModelAndViewResolverMethodReturnValueHandler}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class DefaultMethodReturnValueHandlerTests {
|
public class ModelAndViewResolverMethodReturnValueHandlerTests {
|
||||||
|
|
||||||
private DefaultMethodReturnValueHandler handler;
|
private ModelAndViewResolverMethodReturnValueHandler handler;
|
||||||
|
|
||||||
private List<ModelAndViewResolver> mavResolvers;
|
private List<ModelAndViewResolver> mavResolvers;
|
||||||
|
|
||||||
|
|
@ -56,18 +56,18 @@ public class DefaultMethodReturnValueHandlerTests {
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mavResolvers = new ArrayList<ModelAndViewResolver>();
|
mavResolvers = new ArrayList<ModelAndViewResolver>();
|
||||||
handler = new DefaultMethodReturnValueHandler(mavResolvers);
|
handler = new ModelAndViewResolverMethodReturnValueHandler(mavResolvers);
|
||||||
mavContainer = new ModelAndViewContainer();
|
mavContainer = new ModelAndViewContainer();
|
||||||
request = new ServletWebRequest(new MockHttpServletRequest());
|
request = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modelAndViewResolver() throws Exception {
|
public void modelAndViewResolver() throws Exception {
|
||||||
MethodParameter testBeanType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
||||||
mavResolvers.add(new TestModelAndViewResolver(TestBean.class));
|
mavResolvers.add(new TestModelAndViewResolver(TestBean.class));
|
||||||
TestBean testBean = new TestBean("name");
|
TestBean testBean = new TestBean("name");
|
||||||
|
|
||||||
handler.handleReturnValue(testBean, testBeanType, mavContainer, request);
|
handler.handleReturnValue(testBean, returnType, mavContainer, request);
|
||||||
|
|
||||||
assertEquals("viewName", mavContainer.getViewName());
|
assertEquals("viewName", mavContainer.getViewName());
|
||||||
assertSame(testBean, mavContainer.getModel().get("modelAttrName"));
|
assertSame(testBean, mavContainer.getModel().get("modelAttrName"));
|
||||||
|
|
@ -76,14 +76,15 @@ public class DefaultMethodReturnValueHandlerTests {
|
||||||
|
|
||||||
@Test(expected=UnsupportedOperationException.class)
|
@Test(expected=UnsupportedOperationException.class)
|
||||||
public void modelAndViewResolverUnresolved() throws Exception {
|
public void modelAndViewResolverUnresolved() throws Exception {
|
||||||
MethodParameter testBeanType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("intReturnValue"), -1);
|
||||||
mavResolvers.add(new TestModelAndViewResolver(TestBean.class));
|
mavResolvers.add(new TestModelAndViewResolver(TestBean.class));
|
||||||
handler.handleReturnValue(99, testBeanType, mavContainer, request);
|
handler.handleReturnValue(99, returnType, mavContainer, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handleNull() throws Exception {
|
public void handleNull() throws Exception {
|
||||||
handler.handleReturnValue(null, null, mavContainer, request);
|
MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
||||||
|
handler.handleReturnValue(null, returnType, mavContainer, request);
|
||||||
|
|
||||||
assertNull(mavContainer.getView());
|
assertNull(mavContainer.getView());
|
||||||
assertNull(mavContainer.getViewName());
|
assertNull(mavContainer.getViewName());
|
||||||
|
|
@ -92,14 +93,14 @@ public class DefaultMethodReturnValueHandlerTests {
|
||||||
|
|
||||||
@Test(expected=UnsupportedOperationException.class)
|
@Test(expected=UnsupportedOperationException.class)
|
||||||
public void handleSimpleType() throws Exception {
|
public void handleSimpleType() throws Exception {
|
||||||
MethodParameter intType = new MethodParameter(getClass().getDeclaredMethod("intReturnValue"), -1);
|
MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("intReturnValue"), -1);
|
||||||
handler.handleReturnValue(55, intType, mavContainer, request);
|
handler.handleReturnValue(55, returnType, mavContainer, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handleNonSimpleType() throws Exception{
|
public void handleNonSimpleType() throws Exception{
|
||||||
MethodParameter testBeanType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1);
|
||||||
handler.handleReturnValue(new TestBean(), testBeanType, mavContainer, request);
|
handler.handleReturnValue(new TestBean(), returnType, mavContainer, request);
|
||||||
|
|
||||||
assertTrue(mavContainer.containsAttribute("testBean"));
|
assertTrue(mavContainer.containsAttribute("testBean"));
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,8 @@ import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link WebRequestDataBinder} and initializes it through a {@link WebBindingInitializer}.
|
* Create a {@link WebRequestDataBinder} instance and initialize it with a
|
||||||
|
* {@link WebBindingInitializer}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -30,34 +31,31 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory {
|
||||||
private final WebBindingInitializer initializer;
|
private final WebBindingInitializer initializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create {@link DefaultDataBinderFactory} instance.
|
* Create new instance.
|
||||||
* @param initializer a global initializer to initialize new data binder instances with
|
* @param initializer for global data binder intialization, or {@code null}
|
||||||
*/
|
*/
|
||||||
public DefaultDataBinderFactory(WebBindingInitializer initializer) {
|
public DefaultDataBinderFactory(WebBindingInitializer initializer) {
|
||||||
this.initializer = initializer;
|
this.initializer = initializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link WebDataBinder} for the given target object and initialize it through
|
* Create a new {@link WebDataBinder} for the given target object and
|
||||||
* a {@link WebBindingInitializer}.
|
* initialize it through a {@link WebBindingInitializer}.
|
||||||
*/
|
*/
|
||||||
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
||||||
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
|
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
|
||||||
|
|
||||||
if (initializer != null) {
|
if (initializer != null) {
|
||||||
this.initializer.initBinder(dataBinder, webRequest);
|
this.initializer.initBinder(dataBinder, webRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
initBinder(dataBinder, webRequest);
|
initBinder(dataBinder, webRequest);
|
||||||
|
|
||||||
return dataBinder;
|
return dataBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension hook that subclasses can use to create a data binder of a specific type.
|
* Extension point to create the WebDataBinder instance, which is
|
||||||
* The default implementation creates a {@link WebRequestDataBinder}.
|
* {@link WebRequestDataBinder} by default.
|
||||||
* @param target the data binding target object; or {@code null} for type conversion on simple objects.
|
* @param target the binding target or {@code null} for type conversion only
|
||||||
* @param objectName the name of the target object
|
* @param objectName the binding target object name
|
||||||
* @param webRequest the current request
|
* @param webRequest the current request
|
||||||
*/
|
*/
|
||||||
protected WebDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest webRequest) {
|
protected WebDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest webRequest) {
|
||||||
|
|
@ -65,8 +63,9 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension hook that subclasses can override to initialize further the data binder.
|
* Extension point to further initialize the created data binder instance
|
||||||
* Will be invoked after the data binder is initialized through the {@link WebBindingInitializer}.
|
* (e.g. with {@code @InitBinder} methods) after "global" initializaton
|
||||||
|
* via {@link WebBindingInitializer}.
|
||||||
* @param dataBinder the data binder instance to customize
|
* @param dataBinder the data binder instance to customize
|
||||||
* @param webRequest the current request
|
* @param webRequest the current request
|
||||||
* @throws Exception if initialization fails
|
* @throws Exception if initialization fails
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds data binder initialization through the invocation of @{@link InitBinder} methods.
|
* Adds initialization to a WebDataBinder via {@code @InitBinder} methods.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -41,8 +41,8 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
* @param binderMethods {@link InitBinder} methods to initialize new data binder instances with
|
* @param binderMethods {@code @InitBinder} methods, or {@code null}
|
||||||
* @param initializer a global initializer to initialize new data binder instances with
|
* @param initializer for global data binder intialization
|
||||||
*/
|
*/
|
||||||
public InitBinderDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer initializer) {
|
public InitBinderDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer initializer) {
|
||||||
super(initializer);
|
super(initializer);
|
||||||
|
|
@ -50,16 +50,15 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the given data binder through the invocation of @{@link InitBinder} methods.
|
* Initialize a WebDataBinder with {@code @InitBinder} methods.
|
||||||
* An @{@link InitBinder} method that defines names via {@link InitBinder#value()} will
|
* If the {@code @InitBinder} annotation specifies attributes names, it is
|
||||||
* not be invoked unless one of the names matches the target object name.
|
* invoked only if the names include the target object name.
|
||||||
* @see InitBinder#value()
|
|
||||||
* @throws Exception if one of the invoked @{@link InitBinder} methods fail.
|
* @throws Exception if one of the invoked @{@link InitBinder} methods fail.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
|
public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
|
||||||
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
|
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
|
||||||
if (!isBinderMethodApplicable(binderMethod, binder)) {
|
if (!invokeInitBinderMethod(binderMethod, binder)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Object returnValue = binderMethod.invokeForRequest(request, null, binder);
|
Object returnValue = binderMethod.invokeForRequest(request, null, binder);
|
||||||
|
|
@ -70,12 +69,12 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the given @{@link InitBinder} method should be invoked to initialize
|
* Return {@code true} if the given {@code @InitBinder} method should be
|
||||||
* the given {@link WebDataBinder} instance. This implementations returns {@code true} if
|
* invoked to initialize the given WebDataBinder.
|
||||||
* the @{@link InitBinder} annotation on the method does not define any names or if one of the
|
* <p>The default implementation checks if target object name is included
|
||||||
* names it defines names matches the target object name.
|
* in the attribute names specified in the {@code @InitBinder} annotation.
|
||||||
*/
|
*/
|
||||||
protected boolean isBinderMethodApplicable(HandlerMethod binderMethod, WebDataBinder binder) {
|
protected boolean invokeInitBinderMethod(HandlerMethod binderMethod, WebDataBinder binder) {
|
||||||
InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
|
InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||||
Collection<String> names = Arrays.asList(annot.value());
|
Collection<String> names = Arrays.asList(annot.value());
|
||||||
return (names.size() == 0 || names.contains(binder.getObjectName()));
|
return (names.size() == 0 || names.contains(binder.getObjectName()));
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,15 @@ import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.CookieValue;
|
import org.springframework.web.bind.annotation.CookieValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base abstract class to resolve method arguments annotated with @{@link CookieValue}. Subclasses must define how
|
* A base abstract class to resolve method arguments annotated with
|
||||||
* to extract the cookie value from the request.
|
* {@code @CookieValue}. Subclasses 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
|
* <p>An {@code @CookieValue} is a named value that is resolved from a cookie.
|
||||||
* default value to fall back on when the cookie does not exist. See the base class
|
* It has a required flag and a default value to fall back on when the cookie
|
||||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
* does not exist.
|
||||||
*
|
*
|
||||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved cookie values that don't yet match
|
* <p>A {@link WebDataBinder} may be invoked to apply type conversion to the
|
||||||
* the method parameter type.
|
* resolved cookie value.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
@ -40,8 +40,9 @@ import org.springframework.web.bind.annotation.CookieValue;
|
||||||
public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
* @param beanFactory a bean factory to use for resolving ${...}
|
||||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
* placeholder and #{...} SpEL expressions in default values;
|
||||||
|
* or {@code null} if default values are not expected to contain expressions
|
||||||
*/
|
*/
|
||||||
public AbstractCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
public AbstractCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||||
super(beanFactory);
|
super(beanFactory);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
|
||||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||||
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;
|
||||||
|
|
@ -29,18 +28,18 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract base class adapting a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver}
|
* An abstract base class adapting a {@link WebArgumentResolver} to the
|
||||||
* contract. Provided for backwards compatibility, some important considerations are listed below.
|
* {@link HandlerMethodArgumentResolver} contract.
|
||||||
*
|
|
||||||
* <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
|
* <p><strong>Note:</strong> This class is provided for backwards compatibility.
|
||||||
* model attributes through the {@link ModelAndViewContainer} as well as access to a {@link WebDataBinderFactory}
|
* However it is recommended to re-write a {@code WebArgumentResolver} as
|
||||||
* for when type conversion through a {@link WebDataBinder} is needed.
|
* {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter}
|
||||||
|
* can only be implemented by actually resolving the value and then checking
|
||||||
|
* the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions
|
||||||
|
* raised must be absorbed and ignored since it's not clear whether the adapter
|
||||||
|
* doesn't support the parameter or whether it failed for an internal reason.
|
||||||
|
* The {@code HandlerMethodArgumentResolver} contract also provides access to
|
||||||
|
* model attributes and to {@code WebDataBinderFactory} (for type conversion).
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
@ -53,7 +52,7 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
|
||||||
private final WebArgumentResolver adaptee;
|
private final WebArgumentResolver adaptee;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link AbstractWebArgumentResolverAdapter} with the {@link WebArgumentResolver} instance to delegate to.
|
* Create a new instance.
|
||||||
*/
|
*/
|
||||||
public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
|
public AbstractWebArgumentResolverAdapter(WebArgumentResolver adaptee) {
|
||||||
Assert.notNull(adaptee, "'adaptee' must not be null");
|
Assert.notNull(adaptee, "'adaptee' must not be null");
|
||||||
|
|
@ -61,12 +60,13 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the class-level documentation for an important consideration about exceptions arising in this method.
|
* Actually resolve the value and check the resolved value is not
|
||||||
|
* {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions.
|
||||||
*/
|
*/
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
try {
|
try {
|
||||||
NativeWebRequest webRequest = getWebRequest();
|
NativeWebRequest webRequest = getWebRequest();
|
||||||
Object result = adaptee.resolveArgument(parameter, webRequest);
|
Object result = this.adaptee.resolveArgument(parameter, webRequest);
|
||||||
if (result == WebArgumentResolver.UNRESOLVED) {
|
if (result == WebArgumentResolver.UNRESOLVED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -82,21 +82,21 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide access to a {@link NativeWebRequest}.
|
* Required for access to NativeWebRequest in {@link #supportsParameter}.
|
||||||
*/
|
*/
|
||||||
protected abstract NativeWebRequest getWebRequest();
|
protected abstract NativeWebRequest getWebRequest();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the argument value by delegating to the {@link WebArgumentResolver} instance.
|
* Delegate to the {@link WebArgumentResolver} instance.
|
||||||
* @exception IllegalStateException if the resolved value is {@link WebArgumentResolver#UNRESOLVED} or if the
|
* @exception IllegalStateException if the resolved value is not assignable
|
||||||
* return value type cannot be assigned to the method parameter type.
|
* to the method parameter.
|
||||||
*/
|
*/
|
||||||
public Object resolveArgument(MethodParameter parameter,
|
public Object resolveArgument(MethodParameter parameter,
|
||||||
ModelAndViewContainer mavContainer,
|
ModelAndViewContainer mavContainer,
|
||||||
NativeWebRequest webRequest,
|
NativeWebRequest webRequest,
|
||||||
WebDataBinderFactory binderFactory) throws Exception {
|
WebDataBinderFactory binderFactory) throws Exception {
|
||||||
Class<?> paramType = parameter.getParameterType();
|
Class<?> paramType = parameter.getParameterType();
|
||||||
Object result = adaptee.resolveArgument(parameter, webRequest);
|
Object result = this.adaptee.resolveArgument(parameter, webRequest);
|
||||||
if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
|
if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
|
"Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,12 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments of type {@link Errors} and {@link BindingResult}.
|
* Resolves {@link Errors} method arguments.
|
||||||
*
|
*
|
||||||
* <p>This argument should appear after a model attribute argument in the signature of the handler method.
|
* <p>An {@code Errors} method argument is expected to appear immediately after
|
||||||
* It is resolved by accessing the last attribute in the model expecting that to be a {@link BindingResult}.
|
* the model attribute in the method signature. It is resolved by expecting the
|
||||||
|
* last two attributes added to the model to be the model attribute and its
|
||||||
|
* {@link BindingResult}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -57,8 +59,8 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"An Errors/BindingResult argument must follow a model attribute argument. " +
|
"An Errors/BindingResult argument is expected to be immediately after the model attribute " +
|
||||||
"Check your handler method signature: " + parameter.getMethod());
|
"argument in the controller method signature: " + parameter.getMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -25,14 +25,14 @@ import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments annotated with @{@link Value}.
|
* Resolves method arguments annotated with {@code @Value}.
|
||||||
*
|
*
|
||||||
* <p>An @{@link Value} is a named value that does not have a name but gets resolved from a default value string
|
* <p>An {@code @Value} does not have a name but gets resolved from the default
|
||||||
* that may contain ${...} placeholder or Spring Expression Language #{...} expressions. See the base class
|
* value string, which may contain ${...} placeholder or Spring Expression
|
||||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
* Language #{...} expressions.
|
||||||
*
|
*
|
||||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved argument values that don't yet match
|
* <p>A {@link WebDataBinder} may be invoked to apply type conversion to
|
||||||
* the method parameter type.
|
* resolved argument value.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -40,8 +40,9 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
* @param beanFactory a bean factory to use for resolving ${...}
|
||||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
* placeholder and #{...} SpEL expressions in default values;
|
||||||
|
* or {@code null} if default values are not expected to contain expressions
|
||||||
*/
|
*/
|
||||||
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||||
super(beanFactory);
|
super(beanFactory);
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
@ -36,15 +35,18 @@ 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 @{@link ModelAttribute}. Or if created in default resolution mode,
|
* Resolves method arguments annotated with {@code @ModelAttribute} and handles
|
||||||
* resolves any non-simple type argument even without an @{@link ModelAttribute}. See the constructor for details.
|
* return values from methods annotated with {@code @ModelAttribute}.
|
||||||
|
*
|
||||||
|
* <p>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}.
|
||||||
*
|
*
|
||||||
* <p>A model attribute argument is obtained from the model or otherwise is created with a default constructor.
|
* <p>When this handler is created with {@code annotationNotRequired=true},
|
||||||
* Data binding and validation are applied through a {@link WebDataBinder} instance. Validation is applied
|
* any non-simple type argument and return value is regarded as a model
|
||||||
* only when the argument is also annotated with {@code @Valid}.
|
* attribute with or without the presence of an {@code @ModelAttribute}.
|
||||||
*
|
|
||||||
* <p>Also handles return values from methods annotated with an @{@link ModelAttribute}. The return value is
|
|
||||||
* added to the {@link ModelAndViewContainer}.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -53,26 +55,27 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
|
|
||||||
protected Log logger = LogFactory.getLog(this.getClass());
|
protected Log logger = LogFactory.getLog(this.getClass());
|
||||||
|
|
||||||
private final boolean useDefaultResolution;
|
private final boolean annotationNotRequired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param useDefaultResolution in default resolution mode a method argument that isn't a simple type, as
|
* @param annotationNotRequired if {@code true}, any non-simple type
|
||||||
* defined in {@link BeanUtils#isSimpleProperty(Class)}, is treated as a model attribute even if it doesn't
|
* argument or return value is regarded as a model attribute even without
|
||||||
* have an @{@link ModelAttribute} annotation with its name derived from the model attribute type.
|
* the presence of a {@code @ModelAttribute} annotation in which case the
|
||||||
|
* attribute name is derived from the model attribute's type.
|
||||||
*/
|
*/
|
||||||
public ModelAttributeMethodProcessor(boolean useDefaultResolution) {
|
public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
||||||
this.useDefaultResolution = useDefaultResolution;
|
this.annotationNotRequired = annotationNotRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the parameter is annotated with {@link ModelAttribute} or if it is a
|
* @return true if the parameter is annotated with {@link ModelAttribute}
|
||||||
* simple type without any annotations.
|
* or in default resolution mode also if it is not a simple type.
|
||||||
*/
|
*/
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (this.useDefaultResolution) {
|
else if (this.annotationNotRequired) {
|
||||||
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
|
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -81,13 +84,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the argument to a model attribute looking up the attribute in the model or instantiating it using its
|
* Resolve the argument from the model or if not found instantiate it with
|
||||||
* default constructor. Data binding and optionally validation is then applied through a {@link WebDataBinder}
|
* its default if it is available. The model attribute is then populated
|
||||||
* instance. Validation is invoked optionally when the method parameter is annotated with an {@code @Valid}.
|
* with request values via data binding and optionally validated
|
||||||
*
|
* if {@code @java.validation.Valid} is present on the argument.
|
||||||
* @throws BindException if data binding and validation result in an error and the next method parameter
|
* @throws BindException if data binding and validation result in an error
|
||||||
* is neither of type {@link Errors} nor {@link BindingResult}.
|
* and the next method parameter is not of type {@link Errors}.
|
||||||
* @throws Exception if a {@link WebDataBinder} could not be created.
|
* @throws Exception if WebDataBinder initialization fails.
|
||||||
*/
|
*/
|
||||||
public final Object resolveArgument(MethodParameter parameter,
|
public final Object resolveArgument(MethodParameter parameter,
|
||||||
ModelAndViewContainer mavContainer,
|
ModelAndViewContainer mavContainer,
|
||||||
|
|
@ -119,16 +122,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of the specified model attribute. This method is invoked only if the attribute is
|
* Extension point to create the model attribute if not found in the model.
|
||||||
* not available in the model. This default implementation uses the no-argument constructor.
|
* The default implementation uses the default constructor.
|
||||||
* Subclasses can override to provide additional means of creating the model attribute.
|
* @param attributeName the name of the attribute, never {@code null}
|
||||||
*
|
* @param parameter the method parameter
|
||||||
* @param attributeName the name of the model attribute
|
* @param binderFactory for creating WebDataBinder instance
|
||||||
* @param parameter the method argument declaring the model attribute
|
|
||||||
* @param binderFactory a factory for creating {@link WebDataBinder} instances
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return the created model attribute; never {@code null}
|
* @return the created model attribute, never {@code null}
|
||||||
* @throws Exception raised in the process of creating the instance
|
|
||||||
*/
|
*/
|
||||||
protected Object createAttribute(String attributeName,
|
protected Object createAttribute(String attributeName,
|
||||||
MethodParameter parameter,
|
MethodParameter parameter,
|
||||||
|
|
@ -138,9 +138,8 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the request to the target object contained in the provided binder instance.
|
* Extension point to bind the request to the target object.
|
||||||
*
|
* @param binder the data binder instance to use for the binding
|
||||||
* @param binder the binder with the target object to apply request values to
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
*/
|
*/
|
||||||
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
|
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
|
||||||
|
|
@ -148,12 +147,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to validate the given model attribute argument value.
|
* Whether to validate the model attribute.
|
||||||
* @param argumentValue the validation candidate
|
* The default implementation checks for {@code @javax.validation.Valid}.
|
||||||
* @param parameter the method argument declaring the validation candidate
|
* @param modelAttribute the model attribute
|
||||||
* @return {@code true} if validation should be applied, {@code false} otherwise.
|
* @param parameter the method argument
|
||||||
*/
|
*/
|
||||||
protected boolean isValidationApplicable(Object argumentValue, MethodParameter parameter) {
|
protected boolean isValidationApplicable(Object modelAttribute, MethodParameter parameter) {
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||||
for (Annotation annot : annotations) {
|
for (Annotation annot : annotations) {
|
||||||
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
||||||
|
|
@ -164,10 +163,11 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to raise a {@link BindException} in case of data binding or validation errors.
|
* Whether to raise a {@link BindException} on bind or validation errors.
|
||||||
* @param binder the binder on which validation is to be invoked
|
* The default implementation returns {@code true} if the next method
|
||||||
* @param parameter the method argument for which data binding is performed
|
* argument is not of type {@link Errors}.
|
||||||
* @return true if the binding or validation errors should result in a {@link BindException}, false otherwise.
|
* @param binder the data binder used to perform data binding
|
||||||
|
* @param parameter the method argument
|
||||||
*/
|
*/
|
||||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
||||||
int i = parameter.getParameterIndex();
|
int i = parameter.getParameterIndex();
|
||||||
|
|
@ -177,10 +177,25 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
return !hasBindingResult;
|
return !hasBindingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return {@code true} if there is a method-level {@code @ModelAttribute}
|
||||||
|
* or if it is a non-simple type when {@code annotationNotRequired=true}.
|
||||||
|
*/
|
||||||
public boolean supportsReturnType(MethodParameter returnType) {
|
public boolean supportsReturnType(MethodParameter returnType) {
|
||||||
return returnType.getMethodAnnotation(ModelAttribute.class) != null;
|
if (returnType.getMethodAnnotation(ModelAttribute.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (this.annotationNotRequired) {
|
||||||
|
return !BeanUtils.isSimpleProperty(returnType.getParameterType());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add non-null return values to the {@link ModelAndViewContainer}.
|
||||||
|
*/
|
||||||
public void handleReturnValue(Object returnValue,
|
public void handleReturnValue(Object returnValue,
|
||||||
MethodParameter returnType,
|
MethodParameter returnType,
|
||||||
ModelAndViewContainer mavContainer,
|
ModelAndViewContainer mavContainer,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves {@link Model} method arguments and handles {@link Model} return values.
|
* Resolves {@link Model} arguments and handles {@link Model} return values.
|
||||||
*
|
*
|
||||||
* <p>A {@link Model} return type has a set purpose. Therefore this handler
|
* <p>A {@link Model} return type has a set purpose. Therefore this handler
|
||||||
* should be configured ahead of handlers that support any return value type
|
* should be configured ahead of handlers that support any return value type
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,17 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves {@link Map} method arguments annotated with an @{@link RequestHeader}.
|
* Resolves {@link Map} method arguments annotated with {@code @RequestHeader}.
|
||||||
* See {@link RequestHeaderMethodArgumentResolver} for individual header values with an @{@link RequestHeader}.
|
* For individual header values annotated with {@code @RequestHeader} see
|
||||||
|
* {@link RequestHeaderMethodArgumentResolver} instead.
|
||||||
*
|
*
|
||||||
* <p>The created {@link Map} contains all request header name/value pairs. If the method parameter type
|
* <p>The created {@link Map} contains all request header name/value pairs.
|
||||||
* is {@link MultiValueMap} instead, the created map contains all request headers and all their values in case
|
* The method parameter type may be a {@link MultiValueMap} to receive all
|
||||||
* request headers have multiple values.
|
* values for a header, not only the first one.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see RequestHeaderMethodArgumentResolver
|
|
||||||
*/
|
*/
|
||||||
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,16 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments annotated with @{@link RequestHeader} with the exception of {@link Map} arguments.
|
* Resolves method arguments annotated with {@code @RequestHeader} except for
|
||||||
* See {@link RequestHeaderMapMethodArgumentResolver} for {@link Map} arguments annotated with @{@link RequestHeader}.
|
* {@link Map} arguments. See {@link RequestHeaderMapMethodArgumentResolver} for
|
||||||
|
* details on {@link Map} arguments annotated with {@code @RequestHeader}.
|
||||||
*
|
*
|
||||||
* <p>An @{@link RequestHeader} is a named value that gets resolved from a request header. It has a required flag
|
* <p>An {@code @RequestHeader} is a named value resolved from a request header.
|
||||||
* and a default value to fall back on when the request header does not exist. See the base class
|
* It has a required flag and a default value to fall back on when the request
|
||||||
* {@link AbstractNamedValueMethodArgumentResolver} for more information on how named values are processed.
|
* header does not exist.
|
||||||
*
|
*
|
||||||
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved request header values that
|
* <p>A {@link WebDataBinder} is invoked to apply type conversion to resolved
|
||||||
* don't yet match the method parameter type.
|
* request header values that don't yet match the method parameter type.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
@ -43,8 +44,9 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
|
* @param beanFactory a bean factory to use for resolving ${...}
|
||||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
* placeholder and #{...} SpEL expressions in default values;
|
||||||
|
* or {@code null} if default values are not expected to have expressions
|
||||||
*/
|
*/
|
||||||
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||||
super(beanFactory);
|
super(beanFactory);
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves {@link SessionStatus} arguments by obtaining it from the
|
* Resolves a {@link SessionStatus} argument by obtaining it from
|
||||||
* {@link ModelAndViewContainer}.
|
* the {@link ModelAndViewContainer}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ public class ModelAttributeMethodProcessorTests {
|
||||||
public void supportedReturnTypesInDefaultResolutionMode() throws Exception {
|
public void supportedReturnTypesInDefaultResolutionMode() throws Exception {
|
||||||
processor = new ModelAttributeMethodProcessor(true);
|
processor = new ModelAttributeMethodProcessor(true);
|
||||||
assertTrue(processor.supportsReturnType(returnParamNamedModelAttr));
|
assertTrue(processor.supportsReturnType(returnParamNamedModelAttr));
|
||||||
assertFalse(processor.supportsReturnType(returnParamNonSimpleType));
|
assertTrue(processor.supportsReturnType(returnParamNonSimpleType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue