ModelAndViewContainer related refinements
This commit is contained in:
@@ -44,7 +44,7 @@ import org.springframework.util.ClassUtils;
|
||||
public class HandlerMethod {
|
||||
|
||||
/** Logger that is available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Object bean;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* Defines the algorithm for searching handler methods exhaustively including interfaces and parent
|
||||
* classes while also dealing with parameterized methods and interface and class-based proxies.
|
||||
* classes while also dealing with parameterized methods as well as interface and class-based proxies.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -42,6 +42,7 @@ public abstract class HandlerMethodSelector {
|
||||
*
|
||||
* @param handlerType the handler type to search handler methods on
|
||||
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
|
||||
* @return the selected methods, or an empty set
|
||||
*/
|
||||
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
|
||||
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
|
||||
|
||||
@@ -29,8 +29,8 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* A specialization of {@link DefaultDataBinderFactory} that further initializes {@link WebDataBinder} instances
|
||||
* through the invocation one or more {@link InitBinder} methods.
|
||||
* A specialization of {@link DefaultDataBinderFactory} that further initializes {@link WebDataBinder} instances
|
||||
* by invoking {@link InitBinder} methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -41,7 +41,7 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
|
||||
|
||||
/**
|
||||
* Create an {@code InitBinderMethodDataBinderFactory} instance with the given {@link InitBinder} methods.
|
||||
* @param initBinderMethods {@link InitBinder} methods to use when initializing new data binder instances
|
||||
* @param initBinderMethods {@link InitBinder} methods to use to invoke to initialize new data binder instances
|
||||
* @param bindingInitializer a {@link WebBindingInitializer} to initialize new data binder instances with
|
||||
*/
|
||||
public InitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> initBinderMethods,
|
||||
@@ -51,9 +51,9 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebDataBinder} for the given target object and initialize it through the invocation
|
||||
* of {@link InitBinder} methods. Only {@link InitBinder} annotations that don't specify attributes names
|
||||
* and {@link InitBinder} annotations that contain the target object name are invoked.
|
||||
* Create a {@link WebDataBinder} for the given object and initialize it by calling {@link InitBinder} methods.
|
||||
* Only methods with an {@link InitBinder} annotation value that doesn't list attributes names or methods with
|
||||
* an {@link InitBinder} annotation value that matches the target object name are invoked.
|
||||
* @see InitBinder#value()
|
||||
*/
|
||||
@Override
|
||||
@@ -76,4 +76,4 @@ public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory
|
||||
return dataBinder;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,9 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
@@ -40,17 +38,17 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Provides methods to create and update the "implicit" model for a given request.
|
||||
* Provides methods to create and update a model in the context of a given request.
|
||||
*
|
||||
* <p>{@link #createModel(NativeWebRequest, HandlerMethod)} prepares a model for use with
|
||||
* a {@link RequestMapping} method. The model is populated with handler session attributes as well
|
||||
* as request attributes obtained by invoking model attribute methods.
|
||||
* <p>{@link #initModel(NativeWebRequest, ModelAndViewContainer, HandlerMethod)} populates the model
|
||||
* with handler session attributes and attributes from model attribute methods.
|
||||
*
|
||||
* <p>{@link #updateAttributes(NativeWebRequest, SessionStatus, ModelMap, ModelMap)} updates
|
||||
* the model used for the invocation of a {@link RequestMapping} method, adding handler session
|
||||
* attributes and {@link BindingResult} structures as necessary.
|
||||
* <p>{@link #updateModel(NativeWebRequest, ModelAndViewContainer, SessionStatus)} updates
|
||||
* the model (usually after the {@link RequestMapping} method has been called) promoting attributes
|
||||
* to the session and adding {@link BindingResult} attributes as necessary.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -66,8 +64,8 @@ public final class ModelFactory {
|
||||
/**
|
||||
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
|
||||
* @param attributeMethods {@link ModelAttribute}-annotated methods to invoke when populating a model
|
||||
* @param binderFactory the binder factory to use to add {@link BindingResult} instances to the model
|
||||
* @param sessionHandler a session attributes handler to synch attributes in the model with the session
|
||||
* @param binderFactory the binder factory to use when adding {@link BindingResult}s to the model
|
||||
* @param sessionHandler a session attributes handler to synch attributes with the session
|
||||
*/
|
||||
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
|
||||
WebDataBinderFactory binderFactory,
|
||||
@@ -78,58 +76,78 @@ public final class ModelFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a model for the current request obtaining attributes in the following order:
|
||||
* Populate the model for a request with attributes obtained in the following order:
|
||||
* <ol>
|
||||
* <li>Retrieve previously accessed handler session attributes from the session
|
||||
* <li>Add known (i.e. previously accessed) handler session attributes
|
||||
* <li>Invoke model attribute methods
|
||||
* <li>Find request-handling method {@link ModelAttribute}-annotated arguments that are handler session attributes
|
||||
* <li>Check if any {@link ModelAttribute}-annotated arguments need to be added from the session
|
||||
* </ol>
|
||||
* <p>As a general rule a model attribute is added only once following the above order.
|
||||
* <p>As a general rule model attributes are added only once.
|
||||
*
|
||||
* @param request the current request
|
||||
* @param mavContainer the {@link ModelAndViewContainer} to add model attributes to
|
||||
* @param requestMethod the request handling method for which the model is needed
|
||||
* @return the created model
|
||||
* @throws Exception if an exception occurs while invoking model attribute methods
|
||||
*/
|
||||
public ModelMap createModel(NativeWebRequest request, HandlerMethod requestMethod) throws Exception {
|
||||
ExtendedModelMap model = new BindingAwareModelMap();
|
||||
|
||||
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod requestMethod)
|
||||
throws Exception {
|
||||
|
||||
Map<String, ?> sessionAttributes = this.sessionHandler.retrieveHandlerSessionAttributes(request);
|
||||
model.addAllAttributes(sessionAttributes);
|
||||
mavContainer.addAllAttributes(sessionAttributes);
|
||||
|
||||
invokeAttributeMethods(request, model);
|
||||
invokeAttributeMethods(request, mavContainer);
|
||||
|
||||
addSessionAttributesByName(request, requestMethod, model);
|
||||
|
||||
return model;
|
||||
addSessionAttributesByName(request, mavContainer, requestMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the model by invoking model attribute methods. If two methods provide the same attribute,
|
||||
* the attribute produced by the first method is used.
|
||||
* Invoke model attribute methods to populate the model.
|
||||
* If two methods return the same attribute, the attribute from the first method is added.
|
||||
*/
|
||||
private void invokeAttributeMethods(NativeWebRequest request, ExtendedModelMap model) throws Exception {
|
||||
private void invokeAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
|
||||
throws Exception {
|
||||
|
||||
for (InvocableHandlerMethod attrMethod : this.attributeMethods) {
|
||||
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
|
||||
if (StringUtils.hasText(modelName) && model.containsAttribute(modelName)) {
|
||||
if (mavContainer.containsAttribute(modelName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object returnValue = attrMethod.invokeForRequest(request, model);
|
||||
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
|
||||
|
||||
if (!attrMethod.isVoid()){
|
||||
String valueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
|
||||
if (!model.containsAttribute(valueName)) {
|
||||
model.addAttribute(valueName, returnValue);
|
||||
mavContainer.mergeAttribute(valueName, returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@link ModelAttribute}-annotated arguments are handler session attributes and add them from the session.
|
||||
*/
|
||||
private void addSessionAttributesByName(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod requestMethod) {
|
||||
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
|
||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||
continue;
|
||||
}
|
||||
String attrName = getNameForParameter(parameter);
|
||||
if (!mavContainer.containsAttribute(attrName)) {
|
||||
if (sessionHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
|
||||
Object attrValue = sessionHandler.retrieveAttribute(request, attrName);
|
||||
if (attrValue == null){
|
||||
new HttpSessionRequiredException("Session attribute '" + attrName + "' not found in session");
|
||||
}
|
||||
mavContainer.addAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the model name for the given method return value using one of the following:
|
||||
* Derive the model attribute name for the given return value using one of the following:
|
||||
* <ol>
|
||||
* <li>The method {@link ModelAttribute} annotation value
|
||||
* <li>The name of the return type
|
||||
* <li>The name of the return type
|
||||
* <li>The name of the return value type if the method return type is {@code Object}
|
||||
* </ol>
|
||||
* @param returnValue the value returned from a method invocation
|
||||
@@ -149,29 +167,7 @@ public final class ModelFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find request-handling method, {@link ModelAttribute}-annotated arguments that are handler session attributes
|
||||
* and add them to the model if not present.
|
||||
*/
|
||||
private void addSessionAttributesByName(NativeWebRequest request, HandlerMethod requestMethod, ModelMap model) {
|
||||
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
|
||||
if (!parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||
continue;
|
||||
}
|
||||
String attrName = getNameForParameter(parameter);
|
||||
if (!model.containsKey(attrName)) {
|
||||
if (sessionHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
|
||||
Object attrValue = sessionHandler.retrieveAttribute(request, attrName);
|
||||
if (attrValue == null){
|
||||
new HttpSessionRequiredException("Session attribute '" + attrName + "' not found in session");
|
||||
}
|
||||
model.addAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives the model name for the given method parameter using one of the following:
|
||||
* Derives the model attribute name for the given method parameter using one of the following:
|
||||
* <ol>
|
||||
* <li>The parameter {@link ModelAttribute} annotation value
|
||||
* <li>The name of the parameter type
|
||||
@@ -185,33 +181,29 @@ public final class ModelFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up handler session attributes when {@link SessionStatus#isComplete()} is {@code true}.
|
||||
* Promote model attributes to the session. Add {@link BindingResult} attributes where missing.
|
||||
* Updates the model by cleaning handler session attributes depending on {@link SessionStatus#isComplete()},
|
||||
* promotes model attributes to the session, and adds {@link BindingResult} attributes where missing.
|
||||
* @param request the current request
|
||||
* @param sessionStatus indicates whether handler session attributes are to be cleaned
|
||||
* @param actualModel the model returned from the request method, or {@code null} when the response was handled
|
||||
* @param implicitModel the model for the request
|
||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes a problem
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param sessionStatus whether session processing is complete
|
||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
|
||||
*/
|
||||
public void updateAttributes(NativeWebRequest request,
|
||||
SessionStatus sessionStatus,
|
||||
ModelMap actualModel,
|
||||
ModelMap implicitModel) throws Exception {
|
||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
|
||||
throws Exception {
|
||||
|
||||
if (sessionStatus.isComplete()){
|
||||
this.sessionHandler.cleanupHandlerSessionAttributes(request);
|
||||
}
|
||||
|
||||
if (actualModel != null) {
|
||||
this.sessionHandler.storeHandlerSessionAttributes(request, actualModel);
|
||||
updateBindingResult(request, actualModel);
|
||||
|
||||
this.sessionHandler.storeHandlerSessionAttributes(request, mavContainer.getModel());
|
||||
|
||||
if (mavContainer.isResolveView()) {
|
||||
updateBindingResult(request, mavContainer.getModel());
|
||||
}
|
||||
else {
|
||||
this.sessionHandler.storeHandlerSessionAttributes(request, implicitModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link BindingResult} structures to the model for attributes that require it.
|
||||
* Add {@link BindingResult} attributes to the model for attributes that require it.
|
||||
*/
|
||||
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
|
||||
List<String> keyNames = new ArrayList<String>(model.keySet());
|
||||
|
||||
@@ -174,4 +174,5 @@ public class SessionAttributesHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,13 +25,13 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestScope;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Abstract base class for argument resolvers that resolve named values.
|
||||
@@ -59,7 +59,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
}
|
||||
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* An implementation of {@link HandlerMethodArgumentResolver} that resolves {@link Errors} method parameters.
|
||||
@@ -47,9 +48,10 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
ModelMap model = mavContainer.getModel();
|
||||
if (model.size() > 0) {
|
||||
List<String> keys = new ArrayList<String>(model.keySet());
|
||||
String lastKey = keys.get(model.size()-1);
|
||||
|
||||
@@ -20,13 +20,11 @@ import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.bind.support.WebRequestDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
@@ -77,18 +75,16 @@ public class ModelAttributeMethodProcessor
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebDataBinder} for the target model attribute and applies data binding to it.
|
||||
* The model attribute may be obtained from the "implicit" model, from the session via, or by
|
||||
* direct instantiation.
|
||||
*
|
||||
* @throws Exception if invoking an data binder initialization fails or if data binding and/or
|
||||
* validation results in errors and the next method parameter is not of type {@link Errors}.
|
||||
* Resolves the argument to a model attribute creating a {@link WebDataBinder} and invoking data binding on it.
|
||||
* The model attribute is obtained from the model first or otherwise created via direct instantiation.
|
||||
* @throws Exception if data binder initialization fails or if data binding results in errors and the next
|
||||
* method parameter is not of type {@link Errors}.
|
||||
*/
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
WebDataBinder binder = createDataBinder(parameter, model, webRequest, binderFactory);
|
||||
WebDataBinder binder = createDataBinder(parameter, mavContainer, webRequest, binderFactory);
|
||||
|
||||
if (binder.getTarget() != null) {
|
||||
doBind(binder, webRequest);
|
||||
@@ -102,24 +98,23 @@ public class ModelAttributeMethodProcessor
|
||||
}
|
||||
}
|
||||
|
||||
model.putAll(binder.getBindingResult().getModel());
|
||||
mavContainer.addAllAttributes(binder.getBindingResult().getModel());
|
||||
|
||||
return binder.getTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebDataBinder} for a target object which may be obtained from the "implicit" model,
|
||||
* the session via {@link SessionAttributeStore}, or by direct instantiation.
|
||||
* Creates a {@link WebDataBinder} for a target object.
|
||||
*/
|
||||
private WebDataBinder createDataBinder(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
String attrName = ModelFactory.getNameForParameter(parameter);
|
||||
|
||||
Object target;
|
||||
if (model.containsKey(attrName)) {
|
||||
target = model.get(attrName);
|
||||
if (mavContainer.containsAttribute(attrName)) {
|
||||
target = mavContainer.getAttribute(attrName);
|
||||
}
|
||||
else {
|
||||
target = BeanUtils.instantiateClass(parameter.getParameterType());
|
||||
@@ -166,7 +161,7 @@ public class ModelAttributeMethodProcessor
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
if (returnValue != null) {
|
||||
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
|
||||
mavContainer.addModelAttribute(name, returnValue);
|
||||
mavContainer.addAttribute(name, returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
@@ -46,19 +45,10 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
if (Model.class.isAssignableFrom(paramType)) {
|
||||
return model;
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(paramType)) {
|
||||
return model;
|
||||
}
|
||||
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
return mavContainer.getModel();
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
@@ -78,10 +68,10 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
return;
|
||||
}
|
||||
if (returnValue instanceof Model) {
|
||||
mavContainer.addModelAttributes((Model) returnValue);
|
||||
mavContainer.addAllAttributes(((Model) returnValue).asMap());
|
||||
}
|
||||
else if (returnValue instanceof Map){
|
||||
mavContainer.addModelAttributes((Map) returnValue);
|
||||
mavContainer.addAllAttributes((Map) returnValue);
|
||||
}
|
||||
else {
|
||||
// should not happen
|
||||
|
||||
@@ -22,13 +22,13 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
@@ -48,8 +48,8 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -28,6 +27,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
@@ -52,7 +52,7 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.web.method.annotation.support;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
@@ -28,6 +27,7 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Adapts a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver} contract.
|
||||
@@ -75,7 +75,7 @@ public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
@@ -17,21 +17,20 @@
|
||||
package org.springframework.web.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving method parameters into argument values from a given request.
|
||||
* Strategy interface for resolving method parameters into argument values in the context of a given request.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodArgumentResolver extends HandlerMethodProcessor {
|
||||
public interface HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
|
||||
*
|
||||
* @param parameter the method parameter to check
|
||||
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise
|
||||
@@ -39,20 +38,20 @@ public interface HandlerMethodArgumentResolver extends HandlerMethodProcessor {
|
||||
boolean supportsParameter(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolves a method parameter into an argument value from a given request and a {@link ModelMap} providing
|
||||
* the ability to both access and add new model attributes. A {@link WebDataBinderFactory} is also provided
|
||||
* for creating a {@link WebDataBinder} instance to use for data binding and type conversion.
|
||||
* Resolves a method parameter into an argument value from a given request. A {@link ModelAndViewContainer}
|
||||
* provides access to the model for the request. A {@link WebDataBinderFactory} provides a way to create
|
||||
* a {@link WebDataBinder} instance when needed for data binding and type conversion purposes.
|
||||
*
|
||||
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to
|
||||
* @param parameter the method parameter to resolve. This parameter must have previously been passed to
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param model the model for the current request
|
||||
* @param webRequest the current request.
|
||||
* @param binderFactory a factory in case the resolver needs to create a {@link WebDataBinder} instance
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param webRequest the current request
|
||||
* @param binderFactory a factory for creating {@link WebDataBinder} instances
|
||||
* @return the resolved argument value, or {@code null}.
|
||||
* @throws Exception in case of errors with the preparation of argument values
|
||||
*/
|
||||
Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception;
|
||||
|
||||
|
||||
@@ -24,22 +24,19 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that resolves handler method arguments by delegating
|
||||
* to a list of registered {@link HandlerMethodArgumentResolver}s.
|
||||
*
|
||||
* <p>Previously resolved method argument types are cached internally for faster lookups.
|
||||
* Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s.
|
||||
* Previously resolved method parameters are cached for faster lookups.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverComposite.class);
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final List<HandlerMethodArgumentResolver> argumentResolvers =
|
||||
new ArrayList<HandlerMethodArgumentResolver>();
|
||||
@@ -48,34 +45,34 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>();
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by any of the
|
||||
* registered {@link HandlerMethodArgumentResolver}s.
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
*/
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return getArgumentResolver(parameter) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a method parameter into an argument value for the given request by iterating over registered
|
||||
* {@link HandlerMethodArgumentResolver}s to find one that supports the given method parameter.
|
||||
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
|
||||
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
|
||||
*/
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
|
||||
if (resolver != null) {
|
||||
return resolver.resolveArgument(parameter, model, webRequest, binderFactory);
|
||||
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodArgumentResolver found. " +
|
||||
"supportsParameter(MethodParameter) should have been called previously.");
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodArgumentResolver found. " +
|
||||
"supportsParameter(MethodParameter) should have been called previously.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
|
||||
* @return a {@link HandlerMethodArgumentResolver} instance, or {@code null} if none
|
||||
*/
|
||||
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
|
||||
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
|
||||
@@ -94,15 +91,6 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the argument resolver that supports the given method parameter uses the response argument.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
|
||||
return (resolver != null && resolver.usesResponseArgument(parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given {@link HandlerMethodArgumentResolver}.
|
||||
|
||||
@@ -1,39 +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.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* A base interface for {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodProcessor {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain org.springframework.core.MethodParameter method parameter},
|
||||
* uses the response argument with the implication that the response will be handled directly by invoking
|
||||
* the method and will not require view name resolution followed by rendering.
|
||||
*
|
||||
* @param parameter the method parameter, either a method argument or a return type
|
||||
* @return {@code true} if the supplied parameter uses the response argument; {@code false} otherwise
|
||||
*/
|
||||
boolean usesResponseArgument(MethodParameter parameter);
|
||||
|
||||
}
|
||||
@@ -20,15 +20,15 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface to process the value returned from a handler method invocation.
|
||||
* Strategy interface to handle the value returned from the invocation of a handler method .
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodReturnValueHandler extends HandlerMethodProcessor {
|
||||
public interface HandlerMethodReturnValueHandler {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by this handler.
|
||||
* Whether the given {@linkplain MethodParameter method return type} is supported by this handler.
|
||||
*
|
||||
* @param returnType the method return type to check
|
||||
* @return {@code true} if this handler supports the supplied return type; {@code false} otherwise
|
||||
@@ -36,15 +36,18 @@ public interface HandlerMethodReturnValueHandler extends HandlerMethodProcessor
|
||||
boolean supportsReturnType(MethodParameter returnType);
|
||||
|
||||
/**
|
||||
* Handles the given value returned by a handler method invocation by writing directly to the response
|
||||
* or by using the {@code mavContainer} argument to add model attributes and/or set the view.
|
||||
* Handle the given return value by adding attributes to the model, setting the view (or view name),
|
||||
* or by writing to the response. {@link HandlerMethodReturnValueHandler} implementations should also
|
||||
* consider whether to set {@link ModelAndViewContainer#setResolveView(boolean)}, which is set to
|
||||
* {@code true} by default and therefore needs to be set to {@code false} explicitly if view
|
||||
* resolution is to be bypassed.
|
||||
*
|
||||
* @param returnValue the return value to handle
|
||||
* @param returnType the return type to handle. This type must have previously been passed to
|
||||
* @param returnValue the value returned from the handler method
|
||||
* @param returnType the type of the return value. This type must have previously been passed to
|
||||
* {@link #supportsReturnType(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param mavContainer records model and view choices
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param webRequest the current request
|
||||
* @throws Exception in case of errors
|
||||
* @throws Exception if the return value handling results in an error
|
||||
*/
|
||||
void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
|
||||
@@ -27,17 +27,15 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodReturnValueHandler} that handles method return values by
|
||||
* delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
|
||||
*
|
||||
* <p>Previously resolved return types are cached internally for faster lookups.
|
||||
* Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
|
||||
* Previously resolved return types are cached for faster lookups.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverComposite.class);
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final List<HandlerMethodReturnValueHandler> returnValueHandlers =
|
||||
new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
@@ -46,16 +44,16 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodReturnValueHandler>();
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by any of the
|
||||
* registered {@link HandlerMethodReturnValueHandler}s.
|
||||
* Whether the given {@linkplain MethodParameter method return type} is supported by any registered
|
||||
* {@link HandlerMethodReturnValueHandler}.
|
||||
*/
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return getReturnValueHandler(returnType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the given method return value by iterating over registered {@link HandlerMethodReturnValueHandler}s
|
||||
* to find one that supports it.
|
||||
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
|
||||
* @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
|
||||
*/
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
@@ -64,31 +62,28 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
|
||||
if (handler != null) {
|
||||
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodReturnValueHandler found. " +
|
||||
"supportsReturnType(MethodParameter) should have been called previously");
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodReturnValueHandler found. " +
|
||||
"supportsReturnType(MethodParameter) should have been called previously");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given method return type.
|
||||
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type.
|
||||
*/
|
||||
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
|
||||
if (this.returnValueHandlers == null) {
|
||||
return null;
|
||||
}
|
||||
HandlerMethodReturnValueHandler result = this.returnValueHandlerCache.get(returnType);
|
||||
if (result == null) {
|
||||
for (HandlerMethodReturnValueHandler methodReturnValueHandler : returnValueHandlers) {
|
||||
for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Testing if return value handler [" + methodReturnValueHandler + "] supports [" +
|
||||
logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
|
||||
returnType.getGenericParameterType() + "]");
|
||||
}
|
||||
if (methodReturnValueHandler.supportsReturnType(returnType)) {
|
||||
result = methodReturnValueHandler;
|
||||
this.returnValueHandlerCache.put(returnType, methodReturnValueHandler);
|
||||
if (returnValueHandler.supportsReturnType(returnType)) {
|
||||
result = returnValueHandler;
|
||||
this.returnValueHandlerCache.put(returnType, returnValueHandler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -96,15 +91,6 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the return value handler that supports the given method parameter uses the response.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(parameter);
|
||||
return (handler != null && handler.usesResponseArgument(parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given {@link HandlerMethodReturnValueHandler}.
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
@@ -33,12 +32,15 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
/**
|
||||
* Provides a way to invoke a handler method after resolving its method argument values through
|
||||
* {@link HandlerMethodArgumentResolver}s in the context of the current request.
|
||||
* Provides a method for invoking the handler method for a given request after resolving its method argument
|
||||
* values through registered {@link HandlerMethodArgumentResolver}s.
|
||||
*
|
||||
* <p>Resolving argument values often requires a {@link WebDataBinder} for data binding and type conversion.
|
||||
* Use {@link #setDataBinderFactory(WebDataBinderFactory)} to provide a factory for that. The list of argument
|
||||
* resolvers can be set via {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)}.
|
||||
* <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion.
|
||||
* Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply a binder factory to pass to
|
||||
* argument resolvers.
|
||||
*
|
||||
* <p>Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize
|
||||
* the list of argument resolvers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -52,7 +54,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
/**
|
||||
* Constructs a new invocable handler method with the given bean instance and method.
|
||||
* Constructs a new handler method with the given bean instance and method.
|
||||
* @param bean the bean instance
|
||||
* @param method the method
|
||||
*/
|
||||
@@ -61,7 +63,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new invocable handler method with the given bean instance, method name and parameters.
|
||||
* Constructs a new handler method with the given bean instance, method name and parameters.
|
||||
* @param bean the object bean
|
||||
* @param methodName the method name
|
||||
* @param parameterTypes the method parameter types
|
||||
@@ -73,9 +75,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link WebDataBinderFactory} to be passed to argument resolvers that require a
|
||||
* {@link WebDataBinder} to do type conversion or data binding on the method argument value.
|
||||
*
|
||||
* Sets the {@link WebDataBinderFactory} to be passed to argument resolvers allowing them to create
|
||||
* a {@link WebDataBinder} for data binding and type conversion purposes.
|
||||
* @param dataBinderFactory the data binder factory.
|
||||
*/
|
||||
public void setDataBinderFactory(WebDataBinderFactory dataBinderFactory) {
|
||||
@@ -90,30 +91,30 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
|
||||
* (e.g. for default attribute names).
|
||||
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
* Set the ParameterNameDiscoverer for resolving parameter names when needed (e.g. default request attribute name).
|
||||
* <p>Default is an {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer} instance.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method after resolving its argument values based on the given request.
|
||||
* <p>Most argument values are resolved with the help of {@link HandlerMethodArgumentResolver}s
|
||||
* configured via {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)}.
|
||||
* However, the {@code provideArgs} parameter can be used to supply argument values for use
|
||||
* directly rather than relying on argument resolution - e.g. {@link WebDataBinder},
|
||||
* {@link SessionStatus}, or the thrown exception in a HandlerExceptionResolver.
|
||||
* Invoke the method after resolving its argument values in the context of the given request. <p>Argument
|
||||
* values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
|
||||
* parameter however may supply argument values to be used directly, i.e. without argument resolution.
|
||||
* Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
|
||||
* a thrown exception instance. Provided argument values are checked before argument resolvers.
|
||||
*
|
||||
* @param request the current request
|
||||
* @param model the model used throughout the current request
|
||||
* @param providedArgs argument values to try to use, thus bypassing argument resolution
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param providedArgs argument values to try to use without view resolution
|
||||
* @return the raw value returned by the invoked method
|
||||
* @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
|
||||
*/
|
||||
public final Object invokeForRequest(NativeWebRequest request, ModelMap model, Object... providedArgs)
|
||||
throws Exception {
|
||||
|
||||
Object[] args = getMethodArguments(request, model, providedArgs);
|
||||
public final Object invokeForRequest(NativeWebRequest request,
|
||||
ModelAndViewContainer mavContainer,
|
||||
Object... providedArgs) throws Exception {
|
||||
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
StringBuilder builder = new StringBuilder("Invoking [");
|
||||
@@ -131,8 +132,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private Object[] getMethodArguments(NativeWebRequest request, ModelMap model, Object... providedArgs)
|
||||
throws Exception {
|
||||
/**
|
||||
* Get the method argument values for the current request.
|
||||
*/
|
||||
private Object[] getMethodArgumentValues(NativeWebRequest request,
|
||||
ModelAndViewContainer mavContainer,
|
||||
Object... providedArgs) throws Exception {
|
||||
MethodParameter[] parameters = getMethodParameters();
|
||||
Object[] args = new Object[parameters.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
@@ -141,13 +146,11 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
|
||||
|
||||
args[i] = resolveProvidedArgument(parameter, providedArgs);
|
||||
|
||||
if (args[i] != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.argumentResolvers.supportsParameter(parameter)) {
|
||||
args[i] = this.argumentResolvers.resolveArgument(parameter, model, request, dataBinderFactory);
|
||||
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Cannot resolve argument index=" + parameter.getParameterIndex() + ""
|
||||
@@ -158,6 +161,9 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to resolve a method parameter from the list of provided argument values.
|
||||
*/
|
||||
private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
|
||||
if (providedArgs == null) {
|
||||
return null;
|
||||
@@ -171,10 +177,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes this handler method with the given argument values.
|
||||
* @param args the argument values
|
||||
* @return the result of the invocation
|
||||
* @throws Exception when the method invocation results in an exception
|
||||
* Invoke this handler method with the given argument values.
|
||||
*/
|
||||
private Object invoke(Object... args) throws Exception {
|
||||
ReflectionUtils.makeAccessible(this.getBridgedMethod());
|
||||
@@ -224,19 +227,5 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
throw (Exception) targetException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any of the registered {@link HandlerMethodArgumentResolver}s use the response argument.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
protected boolean usesResponseArgument() {
|
||||
MethodParameter[] methodParameters = getMethodParameters();
|
||||
for (MethodParameter methodParameter : methodParameters) {
|
||||
if (this.argumentResolvers.usesResponseArgument(methodParameter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,16 +18,21 @@ package org.springframework.web.method.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
|
||||
/**
|
||||
* Contains model and view choices made by {@link HandlerMethodReturnValueHandler}s.
|
||||
* Provides access to the model and a place to record model and view related decisions to all
|
||||
* {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s .
|
||||
*
|
||||
* <p>Allows return value handlers to set only the bits that are relevant to them - i.e. model, view,
|
||||
* or none, while also taking care of merging attributes added by the {@link HandlerMethodReturnValueHandler}
|
||||
* with attributes from the implicit model.
|
||||
* <p>In addition to storing model attributes and a view, the {@link ModelAndViewContainer} also provides
|
||||
* a {@link #setResolveView(boolean)} flag, which can be used to request or bypass a view resolution phase.
|
||||
* This is most commonly used from {@link HandlerMethodReturnValueHandler}s but in some cases may also be
|
||||
* used from {@link HandlerMethodArgumentResolver}s such as when a handler method accepts an argument
|
||||
* providing access to the response. When that is the case, if the handler method returns {@code null},
|
||||
* view resolution is skipped.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -38,44 +43,117 @@ public class ModelAndViewContainer {
|
||||
|
||||
private Object view;
|
||||
|
||||
private final ModelMap actualModel = new ExtendedModelMap();
|
||||
private final ModelMap model;
|
||||
|
||||
private final ModelMap implicitModel;
|
||||
private boolean resolveView = true;
|
||||
|
||||
public ModelAndViewContainer(ModelMap implicitModel) {
|
||||
this.implicitModel = (implicitModel != null) ? implicitModel : new ExtendedModelMap();
|
||||
/**
|
||||
* Create a {@link ModelAndViewContainer} instance with a {@link BindingAwareModelMap}.
|
||||
*/
|
||||
public ModelAndViewContainer() {
|
||||
this.model = new BindingAwareModelMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ModelAndViewContainer} instance with the given {@link ModelMap} instance.
|
||||
* @param model the model to use
|
||||
*/
|
||||
public ModelAndViewContainer(ModelMap model) {
|
||||
Assert.notNull(model);
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the model for the current request
|
||||
*/
|
||||
public ModelMap getModel() {
|
||||
return new ExtendedModelMap().addAllAttributes(actualModel).mergeAttributes(implicitModel);
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the view name to use for view resolution, or {@code null}
|
||||
*/
|
||||
public String getViewName() {
|
||||
return this.viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param viewName the name of the view to use for view resolution
|
||||
*/
|
||||
public void setViewName(String viewName) {
|
||||
this.viewName = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the view instance to use for view resolution
|
||||
*/
|
||||
public Object getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param view the view instance to use for view resolution
|
||||
*/
|
||||
public void setView(Object view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
|
||||
*/
|
||||
public boolean isResolveView() {
|
||||
return resolveView;
|
||||
}
|
||||
|
||||
public void addModelAttributes(Model attributes) {
|
||||
actualModel.addAllAttributes(attributes.asMap());
|
||||
/**
|
||||
* @param resolveView whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
|
||||
*/
|
||||
public void setResolveView(boolean resolveView) {
|
||||
this.resolveView = resolveView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether model contains an attribute of the given name.
|
||||
* @param name the name of the model attribute
|
||||
* @return {@code true} if the model contains an attribute by that name and the name is not an empty string
|
||||
*/
|
||||
public boolean containsAttribute(String name) {
|
||||
return (StringUtils.hasText(name) && model.containsAttribute(name));
|
||||
}
|
||||
|
||||
public void addModelAttributes(Map<String, Object> attributes) {
|
||||
actualModel.addAllAttributes(attributes);
|
||||
/**
|
||||
* @param name the attribute to get from the model
|
||||
* @return the attribute or {@code null}
|
||||
*/
|
||||
public Object getAttribute(String name) {
|
||||
return model.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the supplied attribute under the given name.
|
||||
* @param name the name of the model attribute (never null)
|
||||
* @param value the model attribute value (can be null)
|
||||
*/
|
||||
public void addAttribute(String name, Object value) {
|
||||
model.addAttribute(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all attributes in the supplied Map into the model
|
||||
*/
|
||||
public void addAllAttributes(Map<String, ?> attributes) {
|
||||
model.addAllAttributes(attributes);
|
||||
}
|
||||
|
||||
public void addModelAttribute(String name, Object value) {
|
||||
actualModel.addAttribute(name, value);
|
||||
/**
|
||||
* Add the given attribute if the model does not already contain such an attribute.
|
||||
* @param name the name of the attribute to check and add
|
||||
* @param value the value of the attribute
|
||||
*/
|
||||
public void mergeAttribute(String name, Object value) {
|
||||
if (!containsAttribute(name)) {
|
||||
model.addAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user