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:
Rossen Stoyanchev
2011-09-22 16:00:22 +00:00
parent f200ccd899
commit fb526f534a
24 changed files with 880 additions and 524 deletions

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());