POLISH CREATION OF DATA BINDERS FOR @RequestMapping METHODS

Make it possible to hook in custom ServletRequestDataBinderFactory
by overriding RequestMappingHandlerAdapter. 

Create ExtendedServletRequestDataBinder to add URI template vars
to the binding values taking advantage of a new extension hook in
ServletRequestDataBinder to provide additional values to bind.
This commit is contained in:
Rossen Stoyanchev
2011-09-26 09:27:09 +00:00
parent 6bc4ea058c
commit 48f7dcc464
7 changed files with 123 additions and 80 deletions

View File

@@ -0,0 +1,67 @@
/*
* 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.servlet.mvc.method.annotation;
import java.util.Map;
import javax.servlet.ServletRequest;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.HandlerMapping;
/**
* Subclass of {@link ServletRequestDataBinder} that adds URI template variables
* to the values used for data binding.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
/**
* Create a new instance, with default object name.
* @param target the target object to bind onto (or <code>null</code>
* if the binder is just used to convert a plain parameter value)
* @see #DEFAULT_OBJECT_NAME
*/
public ExtendedServletRequestDataBinder(Object target) {
super(target);
}
/**
* Create a new instance.
* @param target the target object to bind onto (or <code>null</code>
* if the binder is just used to convert a plain parameter value)
* @param objectName the name of the target object
* @see #DEFAULT_OBJECT_NAME
*/
public ExtendedServletRequestDataBinder(Object target, String objectName) {
super(target, objectName);
}
/**
* Add URI template variables to the property values used for data binding.
*/
@Override
@SuppressWarnings("unchecked")
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
mpvs.addPropertyValues((Map<String, String>) request.getAttribute(attr));
}
}

View File

@@ -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.
* <p>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<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
/**
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
*/

View File

@@ -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<InvocableHandlerMethod> binderMethods,
WebBindingInitializer initializer) {
public ServletRequestDataBinderFactory(List<InvocableHandlerMethod> 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.
* Returns an instance of {@link ExtendedServletRequestDataBinder}.
*/
@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
*/
@SuppressWarnings("unchecked")
protected final void mergeUriTemplateVariables(MutablePropertyValues mpvs, NativeWebRequest request) {
Map<String, String> uriTemplateVars =
(Map<String, String>) 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));
}
}
}
protected ServletRequestDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest request) {
return new ExtendedServletRequestDataBinder(target, objectName);
}
}