SPR-8859 Fix issue with prototype controllers in RequestMappingHandlerAdapter.
This commit is contained in:
parent
c03a950706
commit
e4fada56ab
|
|
@ -24,6 +24,7 @@ Changes in version 3.1 RC2 (2011-11-15)
|
|||
* added methods to UriComponentsBuilder for replacing the path or the query
|
||||
* added ServletUriComponentsBuilder to build a UriComponents instance starting with a ServletRequest
|
||||
* MockHttpServletRequest and MockHttpServletResponse now keep contentType field and Content-Type header in sync
|
||||
* Fix issue with cache ignoring prototype-scoped controllers in RequestMappingHandlerAdapter
|
||||
|
||||
|
||||
Changes in version 3.1 RC1 (2011-10-11)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
|
@ -143,10 +144,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
|
||||
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
|
||||
|
||||
private final Map<Class<?>, WebDataBinderFactory> dataBinderFactoryCache =
|
||||
new ConcurrentHashMap<Class<?>, WebDataBinderFactory>();
|
||||
private final Map<Class<?>, Set<Method>> dataBinderFactoryCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
|
||||
private final Map<Class<?>, ModelFactory> modelFactoryCache = new ConcurrentHashMap<Class<?>, ModelFactory>();
|
||||
private final Map<Class<?>, Set<Method>> modelFactoryCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
|
|
@ -660,38 +660,38 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
|
||||
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
ModelFactory modelFactory = this.modelFactoryCache.get(handlerType);
|
||||
if (modelFactory == null) {
|
||||
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)) {
|
||||
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
attrMethod.setDataBinderFactory(binderFactory);
|
||||
attrMethods.add(attrMethod);
|
||||
}
|
||||
modelFactory = new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
|
||||
this.modelFactoryCache.put(handlerType, modelFactory);
|
||||
Set<Method> methods = this.modelFactoryCache.get(handlerType);
|
||||
if (methods == null) {
|
||||
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
|
||||
this.modelFactoryCache.put(handlerType, methods);
|
||||
}
|
||||
return modelFactory;
|
||||
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : methods) {
|
||||
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
attrMethod.setDataBinderFactory(binderFactory);
|
||||
attrMethods.add(attrMethod);
|
||||
}
|
||||
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
|
||||
}
|
||||
|
||||
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
WebDataBinderFactory binderFactory = this.dataBinderFactoryCache.get(handlerType);
|
||||
if (binderFactory == null) {
|
||||
List<InvocableHandlerMethod> binderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS)) {
|
||||
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
|
||||
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
|
||||
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
binderMethods.add(binderMethod);
|
||||
}
|
||||
binderFactory = createDataBinderFactory(binderMethods);
|
||||
this.dataBinderFactoryCache.put(handlerType, binderFactory);
|
||||
Set<Method> methods = this.dataBinderFactoryCache.get(handlerType);
|
||||
if (methods == null) {
|
||||
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
this.dataBinderFactoryCache.put(handlerType, methods);
|
||||
}
|
||||
return binderFactory;
|
||||
List<InvocableHandlerMethod> binderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : methods) {
|
||||
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
|
||||
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
|
||||
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
binderMethods.add(binderMethod);
|
||||
}
|
||||
return createDataBinderFactory(binderMethods);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -87,17 +87,14 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
|
||||
/**
|
||||
* Serves as a sandbox to invoke all types of controller methods using all features except for the kitchen sink.
|
||||
* Once a problem has been debugged and understood, tests demonstrating the issue are preferably added to the
|
||||
* appropriate, more fine-grained test fixture.
|
||||
*
|
||||
* <p>If you wish to add high-level tests, consider the following other "integration"-style tests:
|
||||
* <ul>
|
||||
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
||||
* <li>{@link ServletAnnotationControllerHandlerMethodTests}
|
||||
* </ul>
|
||||
* A test fixture with a controller with all supported method signature styles
|
||||
* and arguments. A convenient place to test or confirm a problem with a
|
||||
* specific argument or return value type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @see HandlerMethodAnnotationDetectionTests
|
||||
* @see ServletAnnotationControllerHandlerMethodTests
|
||||
*/
|
||||
public class RequestMappingHandlerAdapterIntegrationTests {
|
||||
|
||||
|
|
@ -261,7 +258,7 @@ public class RequestMappingHandlerAdapterIntegrationTests {
|
|||
@Test
|
||||
public void handleAndCompleteSession() throws Exception {
|
||||
HandlerMethod handlerMethod = handlerMethod("handleAndCompleteSession", SessionStatus.class);
|
||||
ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod);
|
||||
handlerAdapter.handle(request, response, handlerMethod);
|
||||
|
||||
assertFalse(request.getSession().getAttributeNames().hasMoreElements());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ import org.springframework.beans.TestBean;
|
|||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
|
|
@ -1494,6 +1495,29 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
|||
assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prototypeController() throws Exception {
|
||||
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||
public void initialize(GenericWebApplicationContext context) {
|
||||
RootBeanDefinition beanDef = new RootBeanDefinition(PrototypeController.class);
|
||||
beanDef.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("controller", beanDef);
|
||||
}
|
||||
});
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||
request.addParameter("param", "1");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
|
||||
assertEquals("count:3", response.getContentAsString());
|
||||
|
||||
response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
|
||||
assertEquals("count:3", response.getContentAsString());
|
||||
}
|
||||
|
||||
/*
|
||||
* Controllers
|
||||
*/
|
||||
|
|
@ -2828,6 +2852,28 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
|||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class PrototypeController {
|
||||
|
||||
private int count;
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder dataBinder) {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
@ModelAttribute
|
||||
public void populate(Model model) {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public void message(int param, Writer writer) throws IOException {
|
||||
this.count++;
|
||||
writer.write("count:" + this.count);
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases deleted from the original SevletAnnotationControllerTests:
|
||||
|
||||
// @Ignore("Controller interface => no method-level @RequestMapping annotation")
|
||||
|
|
|
|||
Loading…
Reference in New Issue