SPR-8700 REFINE ORDER OF ARGUMENT RESOLUTION AND RETURN VALUE HANDLING.
1. Consider single-purpose return value types like HttpEntity, Model, View, and ModelAndView ahead of annotations like @ResponseBody and @ModelAttribute. And reversely consider multi-purpose return value types like Map, String, and void only after annotations like @RB and @MA. 2. Order custom argument resolvers and return value handlers after the built-in ones also clarifying the fact they cannot be used to override the built-in ones in Javadoc throughout. 3. Provide hooks in RequestMappingHandlerAdapter that subclasses can use to programmatically modify the list of argument resolvers and return value handlers, also adding new getters so subclasses can get access to what they need for the override. 4. Make SessionStatus available through ModelAndViewContainer and provide an argument resolver for it. 5. Init test and javadoc improvements.
This commit is contained in:
@@ -198,13 +198,11 @@ public final class ModelFactory {
|
||||
* promotes model attributes to the session, and adds {@link BindingResult} attributes where missing.
|
||||
* @param request the current request
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param sessionStatus the session status for the current request
|
||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
|
||||
*/
|
||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
|
||||
throws Exception {
|
||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
|
||||
|
||||
if (sessionStatus.isComplete()){
|
||||
if (mavContainer.getSessionStatus().isComplete()){
|
||||
this.sessionAttributesHandler.cleanupAttributes(request);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -55,7 +55,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
*/
|
||||
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
private final ConfigurableBeanFactory configurableBeanFactory;
|
||||
|
||||
private final BeanExpressionContext expressionContext;
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
* in default values, or {@code null} if default values are not expected to contain expressions
|
||||
*/
|
||||
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.configurableBeanFactory = beanFactory;
|
||||
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
|
||||
}
|
||||
|
||||
@@ -105,11 +105,11 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
* Obtain the named value for the given method parameter.
|
||||
*/
|
||||
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
|
||||
NamedValueInfo namedValueInfo = namedValueInfoCache.get(parameter);
|
||||
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
|
||||
if (namedValueInfo == null) {
|
||||
namedValueInfo = createNamedValueInfo(parameter);
|
||||
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
|
||||
namedValueInfoCache.put(parameter, namedValueInfo);
|
||||
this.namedValueInfoCache.put(parameter, namedValueInfo);
|
||||
}
|
||||
return namedValueInfo;
|
||||
}
|
||||
@@ -153,15 +153,15 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
* Resolves the given default value into an argument value.
|
||||
*/
|
||||
private Object resolveDefaultValue(String defaultValue) {
|
||||
if (beanFactory == null) {
|
||||
if (this.configurableBeanFactory == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(defaultValue);
|
||||
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
|
||||
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(defaultValue);
|
||||
BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
|
||||
if (exprResolver == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return exprResolver.evaluate(placeholdersResolved, expressionContext);
|
||||
return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.annotation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
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.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves {@link Map} method arguments and handles {@link Map} return values.
|
||||
*
|
||||
* <p>A Map return value can be interpreted in more than one ways depending
|
||||
* on the presence of annotations like {@code @ModelAttribute} or
|
||||
* {@code @ResponseBody}. Therefore this handler should be configured after
|
||||
* the handlers that support these annotations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return Map.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
return mavContainer.getModel();
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return Map.class.isAssignableFrom(returnType.getParameterType());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
if (returnValue == null) {
|
||||
return;
|
||||
}
|
||||
else if (returnValue instanceof Map){
|
||||
mavContainer.addAllAttributes((Map) returnValue);
|
||||
}
|
||||
else {
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.web.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
@@ -29,12 +25,12 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves {@link Map} and {@link Model} method arguments.
|
||||
* Resolves {@link Model} method arguments and handles {@link Model} return values.
|
||||
*
|
||||
* <p>Handles {@link Model} return values adding their attributes to the {@link ModelAndViewContainer}.
|
||||
* Handles {@link Map} return values in the same way as long as the method does not have an @{@link ModelAttribute}.
|
||||
* If the method does have an @{@link ModelAttribute}, it is assumed the returned {@link Map} is a model attribute
|
||||
* and not a model.
|
||||
* <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
|
||||
* annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure
|
||||
* they don't take over.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
@@ -42,8 +38,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType);
|
||||
return Model.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
@@ -54,12 +49,9 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> paramType = returnType.getParameterType();
|
||||
boolean hasModelAttr = returnType.getMethodAnnotation(ModelAttribute.class) != null;
|
||||
return (Model.class.isAssignableFrom(paramType) || (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
|
||||
return Model.class.isAssignableFrom(returnType.getParameterType());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer,
|
||||
@@ -67,17 +59,13 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
if (returnValue == null) {
|
||||
return;
|
||||
}
|
||||
if (returnValue instanceof Model) {
|
||||
else if (returnValue instanceof Model) {
|
||||
mavContainer.addAllAttributes(((Model) returnValue).asMap());
|
||||
}
|
||||
else if (returnValue instanceof Map){
|
||||
mavContainer.addAllAttributes((Map) returnValue);
|
||||
}
|
||||
else {
|
||||
// should not happen
|
||||
Method method = returnType.getMethod();
|
||||
String returnTypeName = returnType.getParameterType().getName();
|
||||
throw new UnsupportedOperationException("Unknown return type: " + returnTypeName + " in method: " + method);
|
||||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Resolves {@link SessionStatus} arguments by obtaining it from the
|
||||
* {@link ModelAndViewContainer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SessionStatusMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return SessionStatus.class.equals(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
return mavContainer.getSessionStatus();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.method.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -45,6 +46,13 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>();
|
||||
|
||||
/**
|
||||
* Return a read-only list with the contained resolvers, or an empty list.
|
||||
*/
|
||||
public List<HandlerMethodArgumentResolver> getResolvers() {
|
||||
return Collections.unmodifiableList(this.argumentResolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
@@ -90,19 +98,22 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
/**
|
||||
* Add the given {@link HandlerMethodArgumentResolver}.
|
||||
*/
|
||||
public void addResolver(HandlerMethodArgumentResolver argumentResolver) {
|
||||
public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver argumentResolver) {
|
||||
this.argumentResolvers.add(argumentResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given {@link HandlerMethodArgumentResolver}s.
|
||||
*/
|
||||
public void addResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
public HandlerMethodArgumentResolverComposite addResolvers(
|
||||
List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
if (argumentResolvers != null) {
|
||||
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
|
||||
this.argumentResolvers.add(resolver);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.method.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -44,6 +45,13 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
private final Map<MethodParameter, HandlerMethodReturnValueHandler> returnValueHandlerCache =
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodReturnValueHandler>();
|
||||
|
||||
/**
|
||||
* Return a read-only list with the registered handlers, or an empty list.
|
||||
*/
|
||||
public List<HandlerMethodReturnValueHandler> getHandlers() {
|
||||
return Collections.unmodifiableList(this.returnValueHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given {@linkplain MethodParameter method return type} is supported by any registered
|
||||
* {@link HandlerMethodReturnValueHandler}.
|
||||
@@ -89,19 +97,22 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
/**
|
||||
* Add the given {@link HandlerMethodReturnValueHandler}.
|
||||
*/
|
||||
public void addHandler(HandlerMethodReturnValueHandler returnValuehandler) {
|
||||
public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler returnValuehandler) {
|
||||
returnValueHandlers.add(returnValuehandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given {@link HandlerMethodReturnValueHandler}s.
|
||||
*/
|
||||
public void addHandlers(List<? extends HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
public HandlerMethodReturnValueHandlerComposite addHandlers(
|
||||
List<? extends HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
if (returnValueHandlers != null) {
|
||||
for (HandlerMethodReturnValueHandler handler : returnValueHandlers) {
|
||||
this.returnValueHandlers.add(handler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import java.util.Map;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
|
||||
/**
|
||||
* Records model and view related decisions made by
|
||||
@@ -33,7 +35,7 @@ import org.springframework.validation.support.BindingAwareModelMap;
|
||||
*
|
||||
* <p>A default {@link Model} is automatically created at instantiation.
|
||||
* An alternate model instance may be provided via {@link #setRedirectModel}
|
||||
* for use in a redirect scenario. When {@link #setUseRedirectModel} is set
|
||||
* for use in a redirect scenario. When {@link #setRedirectModelScenario} is set
|
||||
* to {@code true} signalling a redirect scenario, the {@link #getModel()}
|
||||
* returns the redirect model instead of the default model.
|
||||
*
|
||||
@@ -46,14 +48,16 @@ public class ModelAndViewContainer {
|
||||
|
||||
private boolean requestHandled = false;
|
||||
|
||||
private final ModelMap model = new BindingAwareModelMap();
|
||||
private final ModelMap defaultModel = new BindingAwareModelMap();
|
||||
|
||||
private ModelMap redirectModel;
|
||||
|
||||
private boolean ignoreDefaultModelOnRedirect = false;
|
||||
|
||||
private boolean useRedirectModel = false;
|
||||
private boolean redirectModelScenario = false;
|
||||
|
||||
private boolean ignoreDefaultModelOnRedirect = false;
|
||||
|
||||
private final SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
@@ -123,34 +127,45 @@ public class ModelAndViewContainer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the model to use. This is either the default model created at
|
||||
* instantiation or the redirect model if {@link #setUseRedirectModel}
|
||||
* is set to {@code true}. If a redirect model was never provided via
|
||||
* {@link #setRedirectModel}, return the default model unless
|
||||
* {@link #setIgnoreDefaultModelOnRedirect} is set to {@code true}.
|
||||
* Return the model to use: the "default" or the "redirect" model.
|
||||
* <p>The default model is used if {@code "redirectModelScenario=false"} or
|
||||
* if the redirect model is {@code null} (i.e. it wasn't declared as a
|
||||
* method argument) and {@code ignoreDefaultModelOnRedirect=false}.
|
||||
*/
|
||||
public ModelMap getModel() {
|
||||
if (!this.useRedirectModel) {
|
||||
return this.model;
|
||||
}
|
||||
else if (this.redirectModel != null) {
|
||||
return this.redirectModel;
|
||||
if (useDefaultModel()) {
|
||||
return this.defaultModel;
|
||||
}
|
||||
else {
|
||||
return this.ignoreDefaultModelOnRedirect ? new ModelMap() : this.model;
|
||||
return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the default model or the redirect model.
|
||||
*/
|
||||
private boolean useDefaultModel() {
|
||||
return !this.redirectModelScenario || ((this.redirectModel == null) && !this.ignoreDefaultModelOnRedirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a separate model instance to use in a redirect scenario.
|
||||
* The provided additional model however is not used used unless
|
||||
* {@link #setUseRedirectModel(boolean)} gets set to {@code true} to signal
|
||||
* {@link #setRedirectModelScenario(boolean)} gets set to {@code true} to signal
|
||||
* a redirect scenario.
|
||||
*/
|
||||
public void setRedirectModel(ModelMap redirectModel) {
|
||||
this.redirectModel = redirectModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the conditions are in place for using a redirect model.
|
||||
* Typically that means the controller has returned a redirect instruction.
|
||||
*/
|
||||
public void setRedirectModelScenario(boolean redirectModelScenario) {
|
||||
this.redirectModelScenario = redirectModelScenario;
|
||||
}
|
||||
|
||||
/**
|
||||
* When set to {@code true} the default model is never used in a redirect
|
||||
* scenario. So if a redirect model is not available, an empty model is
|
||||
@@ -164,11 +179,11 @@ public class ModelAndViewContainer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the conditions for using a redirect model are in place -- e.g.
|
||||
* the controller has requested a redirect.
|
||||
* Return the {@link SessionStatus} instance to use that can be used to
|
||||
* signal that session processing is complete.
|
||||
*/
|
||||
public void setUseRedirectModel(boolean useRedirectModel) {
|
||||
this.useRedirectModel = useRedirectModel;
|
||||
public SessionStatus getSessionStatus() {
|
||||
return sessionStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +244,13 @@ public class ModelAndViewContainer {
|
||||
else {
|
||||
sb.append("View is [").append(this.view).append(']');
|
||||
}
|
||||
sb.append("; model is ").append(getModel());
|
||||
if (useDefaultModel()) {
|
||||
sb.append("; default model ");
|
||||
}
|
||||
else {
|
||||
sb.append("; redirect model ");
|
||||
}
|
||||
sb.append(getModel());
|
||||
}
|
||||
else {
|
||||
sb.append("Request handled directly");
|
||||
|
||||
@@ -41,8 +41,6 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
@@ -161,7 +159,7 @@ public class ModelFactoryTests {
|
||||
replay(binderFactory);
|
||||
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus());
|
||||
modelFactory.updateModel(webRequest, mavContainer);
|
||||
|
||||
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
|
||||
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
|
||||
@@ -177,6 +175,7 @@ public class ModelFactoryTests {
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
mavContainer.addAttribute(attrName, attrValue);
|
||||
mavContainer.getSessionStatus().setComplete();
|
||||
sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
|
||||
|
||||
// Resolve successfully handler session attribute once
|
||||
@@ -187,11 +186,8 @@ public class ModelFactoryTests {
|
||||
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
sessionStatus.setComplete();
|
||||
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
|
||||
modelFactory.updateModel(webRequest, mavContainer);
|
||||
|
||||
assertEquals(attrValue, mavContainer.getModel().get(attrName));
|
||||
assertNull(sessionAttributeStore.retrieveAttribute(webRequest, attrName));
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link MapMethodProcessor}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MapMethodProcessorTests {
|
||||
|
||||
private MapMethodProcessor processor;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
private MethodParameter paramMap;
|
||||
|
||||
private MethodParameter returnParamMap;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
processor = new MapMethodProcessor();
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("map", Map.class);
|
||||
paramMap = new MethodParameter(method, 0);
|
||||
returnParamMap = new MethodParameter(method, 0);
|
||||
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(processor.supportsParameter(paramMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue(processor.supportsReturnType(returnParamMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentValue() throws Exception {
|
||||
assertSame(mavContainer.getModel(), processor.resolveArgument(paramMap, mavContainer, webRequest, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMapReturnValue() throws Exception {
|
||||
mavContainer.addAttribute("attr1", "value1");
|
||||
Map<String, Object> returnValue = new ModelMap("attr2", "value2");
|
||||
|
||||
processor.handleReturnValue(returnValue , returnParamMap, mavContainer, webRequest);
|
||||
|
||||
assertEquals("value1", mavContainer.getModel().get("attr1"));
|
||||
assertEquals("value2", mavContainer.getModel().get("attr2"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Map<String, Object> map(Map<String, Object> map) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,14 +21,13 @@ import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
@@ -48,10 +47,6 @@ public class ModelMethodProcessorTests {
|
||||
|
||||
private MethodParameter returnParamModel;
|
||||
|
||||
private MethodParameter paramMap;
|
||||
|
||||
private MethodParameter returnParamMap;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
@@ -63,64 +58,39 @@ public class ModelMethodProcessorTests {
|
||||
paramModel = new MethodParameter(method, 0);
|
||||
returnParamModel = new MethodParameter(method, -1);
|
||||
|
||||
method = getClass().getDeclaredMethod("map", Map.class);
|
||||
paramMap = new MethodParameter(method, 0);
|
||||
returnParamMap = new MethodParameter(method, 0);
|
||||
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(processor.supportsParameter(paramModel));
|
||||
assertTrue(processor.supportsParameter(paramMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue(processor.supportsReturnType(returnParamModel));
|
||||
assertTrue(processor.supportsReturnType(returnParamMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentValue() throws Exception {
|
||||
Object result = processor.resolveArgument(paramModel, mavContainer, webRequest, null);
|
||||
assertSame(mavContainer.getModel(), result);
|
||||
|
||||
result = processor.resolveArgument(paramMap, mavContainer, webRequest, null);
|
||||
assertSame(mavContainer.getModel(), result);
|
||||
assertSame(mavContainer.getModel(), processor.resolveArgument(paramModel, mavContainer, webRequest, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleModelReturnValue() throws Exception {
|
||||
mavContainer.addAttribute("attr1", "value1");
|
||||
ModelMap returnValue = new ModelMap("attr2", "value2");
|
||||
Model returnValue = new ExtendedModelMap();
|
||||
returnValue.addAttribute("attr2", "value2");
|
||||
|
||||
processor.handleReturnValue(returnValue , returnParamModel, mavContainer, webRequest);
|
||||
|
||||
assertEquals("value1", mavContainer.getModel().get("attr1"));
|
||||
assertEquals("value2", mavContainer.getModel().get("attr2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMapReturnValue() throws Exception {
|
||||
mavContainer.addAttribute("attr1", "value1");
|
||||
Map<String, Object> returnValue = new ModelMap("attr2", "value2");
|
||||
|
||||
processor.handleReturnValue(returnValue , returnParamMap, mavContainer, webRequest);
|
||||
|
||||
assertEquals("value1", mavContainer.getModel().get("attr1"));
|
||||
assertEquals("value2", mavContainer.getModel().get("attr2"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Model model(Model model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Map<String, Object> map(Map<String, Object> map) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class ModelAndViewContainerTests {
|
||||
assertEquals("Default model should be used if not in redirect scenario",
|
||||
"value", this.mavContainer.getModel().get("name"));
|
||||
|
||||
this.mavContainer.setUseRedirectModel(true);
|
||||
this.mavContainer.setRedirectModelScenario(true);
|
||||
|
||||
assertEquals("Redirect model should be used in redirect scenario",
|
||||
"redirectValue", this.mavContainer.getModel().get("name"));
|
||||
@@ -61,7 +61,7 @@ public class ModelAndViewContainerTests {
|
||||
@Test
|
||||
public void getModelIgnoreDefaultModelOnRedirect() {
|
||||
this.mavContainer.addAttribute("name", "value");
|
||||
this.mavContainer.setUseRedirectModel(true);
|
||||
this.mavContainer.setRedirectModelScenario(true);
|
||||
|
||||
assertEquals("Default model should be used since no redirect model was provided",
|
||||
1, this.mavContainer.getModel().size());
|
||||
|
||||
Reference in New Issue
Block a user