) request.getAttribute(attr));
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
index 46ebe008287..6b48574531e 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
@@ -676,7 +676,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
return modelFactory;
}
- private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) {
+ private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class> handlerType = handlerMethod.getBeanType();
WebDataBinderFactory binderFactory = this.dataBinderFactoryCache.get(handlerType);
if (binderFactory == null) {
@@ -688,12 +688,25 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
binderMethods.add(binderMethod);
}
- binderFactory = new ServletRequestDataBinderFactory(binderMethods, this.webBindingInitializer);
+ binderFactory = createDataBinderFactory(binderMethods);
this.dataBinderFactoryCache.put(handlerType, binderFactory);
}
return binderFactory;
}
+ /**
+ * Template method to create a new ServletRequestDataBinderFactory instance.
+ * The default implementation creates a ServletRequestDataBinderFactory.
+ * This can be overridden for custom ServletRequestDataBinder subclasses.
+ * @param binderMethods {@code @InitBinder} methods
+ * @return the ServletRequestDataBinderFactory instance to use
+ * @throws Exception in case of invalid state or arguments
+ */
+ protected ServletRequestDataBinderFactory createDataBinderFactory(List binderMethods)
+ throws Exception {
+ return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
+ }
+
/**
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
*/
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactory.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactory.java
index d807cb55e99..f43a7c9f8ad 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactory.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactory.java
@@ -17,21 +17,15 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.List;
-import java.util.Map;
-import org.springframework.beans.MutablePropertyValues;
import org.springframework.web.bind.ServletRequestDataBinder;
-import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.NativeWebRequest;
-import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
import org.springframework.web.method.support.InvocableHandlerMethod;
-import org.springframework.web.servlet.HandlerMapping;
/**
- * Creates a WebDataBinder of type {@link ServletRequestDataBinder} that can
- * also use URI template variables values for data binding purposes.
+ * Creates a {@code ServletRequestDataBinder}.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -43,45 +37,16 @@ public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory
* @param binderMethods one or more {@code @InitBinder} methods
* @param initializer provides global data binder initialization
*/
- public ServletRequestDataBinderFactory(List binderMethods,
- WebBindingInitializer initializer) {
+ public ServletRequestDataBinderFactory(List binderMethods, WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
-
- /**
- * Create a WebDataBinder of type {@link ServletRequestDataBinder} that can
- * also use URI template variables values for data binding purposes.
- */
- @Override
- protected WebDataBinder createBinderInstance(Object target, String objectName, final NativeWebRequest request) {
- return new ServletRequestDataBinder(target, objectName) {
- @Override
- protected void doBind(MutablePropertyValues mpvs) {
- mergeUriTemplateVariables(mpvs, request);
- super.doBind(mpvs);
- }
- };
- }
/**
- * Merge URI variable values into the given PropertyValues.
- * @param mpvs the PropertyValues to add to
- * @param request the current request
+ * Returns an instance of {@link ExtendedServletRequestDataBinder}.
*/
- @SuppressWarnings("unchecked")
- protected final void mergeUriTemplateVariables(MutablePropertyValues mpvs, NativeWebRequest request) {
-
- Map uriTemplateVars =
- (Map) request.getAttribute(
- HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
-
- if (uriTemplateVars != null){
- for (String variableName : uriTemplateVars.keySet()) {
- if (!mpvs.contains(variableName)) {
- mpvs.addPropertyValue(variableName, uriTemplateVars.get(variableName));
- }
- }
- }
+ @Override
+ protected ServletRequestDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest request) {
+ return new ExtendedServletRequestDataBinder(target, objectName);
}
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactoryTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinderTests.java
similarity index 68%
rename from org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactoryTests.java
rename to org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinderTests.java
index 10da357019e..f65e034af71 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestDataBinderFactoryTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinderTests.java
@@ -21,42 +21,26 @@ import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.TestBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.context.request.NativeWebRequest;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerMapping;
/**
- * Test fixture with {@link ServletRequestDataBinderFactory}.
+ * Test fixture for {@link ExtendedServletRequestDataBinder}.
*
* @author Rossen Stoyanchev
*/
-public class ServletRequestDataBinderFactoryTests {
+public class ExtendedServletRequestDataBinderTests {
- private ServletRequestDataBinderFactory binderFactory;
-
private MockHttpServletRequest request;
- private NativeWebRequest webRequest;
-
@Before
public void setup() {
- binderFactory = new ServletRequestDataBinderFactory(null, null);
- request = new MockHttpServletRequest();
- webRequest = new ServletWebRequest(request);
- RequestContextHolder.setRequestAttributes(webRequest);
- }
-
- @After
- public void teardown() {
- RequestContextHolder.resetRequestAttributes();
+ this.request = new MockHttpServletRequest();
}
@Test
@@ -67,7 +51,7 @@ public class ServletRequestDataBinderFactoryTests {
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
TestBean target = new TestBean();
- WebDataBinder binder = binderFactory.createBinder(webRequest, target, "");
+ WebDataBinder binder = new ExtendedServletRequestDataBinder(target, "");
((ServletRequestDataBinder) binder).bind(request);
assertEquals("nameValue", target.getName());
@@ -75,7 +59,7 @@ public class ServletRequestDataBinderFactoryTests {
}
@Test
- public void requestParamsOverrideUriTemplateVars() throws Exception {
+ public void uriTemplateVarAndRequestParam() throws Exception {
request.addParameter("age", "35");
Map uriTemplateVars = new HashMap();
@@ -84,17 +68,17 @@ public class ServletRequestDataBinderFactoryTests {
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
TestBean target = new TestBean();
- WebDataBinder binder = binderFactory.createBinder(webRequest, target, "");
+ WebDataBinder binder = new ExtendedServletRequestDataBinder(target, "");
((ServletRequestDataBinder) binder).bind(request);
assertEquals("nameValue", target.getName());
- assertEquals(35, target.getAge());
+ assertEquals(25, target.getAge());
}
@Test
public void noUriTemplateVars() throws Exception {
TestBean target = new TestBean();
- WebDataBinder binder = binderFactory.createBinder(webRequest, target, "");
+ WebDataBinder binder = new ExtendedServletRequestDataBinder(target, "");
((ServletRequestDataBinder) binder).bind(request);
assertEquals(null, target.getName());
diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java b/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java
index f488024d9b6..838cc633b2a 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java
@@ -108,9 +108,20 @@ public class ServletRequestDataBinder extends WebDataBinder {
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
+ addBindValues(mpvs, request);
doBind(mpvs);
}
+ /**
+ * Extension point that subclasses can use to add extra bind values for a
+ * request. Invoked before {@link #doBind(MutablePropertyValues)}.
+ * The default implementation is empty.
+ * @param mpvs the property values that will be used for data binding
+ * @param request the current request
+ */
+ protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
+ }
+
/**
* Treats errors as fatal.
* Use this method only if it's an error if the input isn't valid.
diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java b/org.springframework.web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java
index 55e7d2db0ab..a0ddc9713fc 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java
@@ -41,10 +41,12 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory {
/**
* Create a new {@link WebDataBinder} for the given target object and
* initialize it through a {@link WebBindingInitializer}.
+ * @throws Exception in case of invalid state or arguments
*/
- public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
+ public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
+ throws Exception {
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
- if (initializer != null) {
+ if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
initBinder(dataBinder, webRequest);
@@ -52,13 +54,15 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory {
}
/**
- * Extension point to create the WebDataBinder instance, which is
- * {@link WebRequestDataBinder} by default.
+ * Extension point to create the WebDataBinder instance.
+ * By default this is {@code WebRequestDataBinder}.
* @param target the binding target or {@code null} for type conversion only
* @param objectName the binding target object name
* @param webRequest the current request
+ * @throws Exception in case of invalid state or arguments
*/
- protected WebDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest webRequest) {
+ protected WebDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest webRequest)
+ throws Exception {
return new WebRequestDataBinder(target, objectName);
}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/InitBinderDataBinderFactory.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/InitBinderDataBinderFactory.java
index a85fda6fa4d..991ab9ff862 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/InitBinderDataBinderFactory.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/InitBinderDataBinderFactory.java
@@ -58,12 +58,11 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
@Override
public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
- if (!invokeInitBinderMethod(binderMethod, binder)) {
- continue;
- }
- Object returnValue = binderMethod.invokeForRequest(request, null, binder);
- if (returnValue != null) {
- throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
+ if (isBinderMethodApplicable(binderMethod, binder)) {
+ Object returnValue = binderMethod.invokeForRequest(request, null, binder);
+ if (returnValue != null) {
+ throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
+ }
}
}
}
@@ -74,8 +73,8 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
*
The default implementation checks if target object name is included
* in the attribute names specified in the {@code @InitBinder} annotation.
*/
- protected boolean invokeInitBinderMethod(HandlerMethod binderMethod, WebDataBinder binder) {
- InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
+ protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder binder) {
+ InitBinder annot = initBinderMethod.getMethodAnnotation(InitBinder.class);
Collection names = Arrays.asList(annot.value());
return (names.size() == 0 || names.contains(binder.getObjectName()));
}