需求:
将http报文请求(保护body和url)中的参数传递给Controller时支持使用别名。
举例:
下面两条请求报文的结果是一致的。
http://example.com/foo?jobType=permanent&location=Stockholm
http://example.com/foo?jt=permanent&loc=Stockholm
返回响应
解决方法
Spring MVC3 提供了丰富的参数映射机制, 详细信息可以参见这里
同时对于Spring MVC默认的提供的映射机制不能涵盖的对象,我们可以通过扩展HandlerMethodArgumentResolver和HttpMessageConverter的机制来实现。
HandlerMethodArgumentResolver对应输入, HttpMessageConverter对应输出
1.预备代码
package com.davidwang456.web.model; import com.davidwang456.web.annotation.ParamName; public class Job { @ParamName("jt") private String jobType; @ParamName("loc") private String location; public String getJobType() { return jobType; } public void setJobType(String jobType) { this.jobType = jobType; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } @Override public String toString(){ return "jobType="+jobType+",location="+location; } }
2.注解
package com.davidwang456.web.annotation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; /** * Overrides parameter name */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ParamName { /** * The name of the request parameter to bind to. */ String value(); }
3.注解处理器
package com.davidwang456.web.processor; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; import com.davidwang456.web.annotation.ParamName; /** * Method processor supports {@link ParamName} parameters renaming * */ public class RenamingProcessor extends ServletModelAttributeMethodProcessor { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; //Rename cache private final Map<Class<?>, Map<String, String>> replaceMap = new ConcurrentHashMap<Class<?>, Map<String, String>>(); public RenamingProcessor(boolean annotationNotRequired) { super(annotationNotRequired); } @Override protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest nativeWebRequest) { Object target = binder.getTarget(); Class<?> targetClass = target.getClass(); if (!replaceMap.containsKey(targetClass)) { Map<String, String> mapping = analyzeClass(targetClass); replaceMap.put(targetClass, mapping); } Map<String, String> mapping = replaceMap.get(targetClass); ParamNameDataBinder paramNameDataBinder = new ParamNameDataBinder(target, binder.getObjectName(), mapping); requestMappingHandlerAdapter.getWebBindingInitializer().initBinder(paramNameDataBinder, nativeWebRequest); super.bindRequestParameters(paramNameDataBinder, nativeWebRequest); } private static Map<String, String> analyzeClass(Class<?> targetClass) { Field[] fields = targetClass.getDeclaredFields(); Map<String, String> renameMap = new HashMap<String, String>(); for (Field field : fields) { ParamName paramNameAnnotation = field.getAnnotation(ParamName.class); if (paramNameAnnotation != null && !paramNameAnnotation.value().isEmpty()) { renameMap.put(paramNameAnnotation.value(), field.getName()); } } if (renameMap.isEmpty()) return Collections.emptyMap(); return renameMap; } }
4.数据绑定
package com.davidwang456.web.processor; import java.util.Map; import javax.servlet.ServletRequest; import org.springframework.beans.MutablePropertyValues; import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder; /** * ServletRequestDataBinder which supports fields renaming using {@link ParamName} * */ public class ParamNameDataBinder extends ExtendedServletRequestDataBinder { private final Map<String, String> renameMapping; public ParamNameDataBinder(Object target, String objectName, Map<String, String> renameMapping) { super(target, objectName); this.renameMapping = renameMapping; } @Override protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { super.addBindValues(mpvs, request); for (Map.Entry<String, String> entry : renameMapping.entrySet()) { String from = entry.getKey(); String to = entry.getValue(); if (mpvs.contains(from)) { mpvs.add(to, mpvs.getPropertyValue(from).getValue()); } } } }
5.注册处理器
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.davidwang456.web.processor.RenamingProcessor">
<constructor-arg name="annotationNotRequired" value="true"/>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
或者java config模式
package com.davidwang456.web.configurer; import java.util.List; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.davidwang456.web.processor.RenamingProcessor; @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter{ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { RenamingProcessor renameResolver = new RenamingProcessor(true); argumentResolvers.add(renameResolver); } }
参考文献:
【1】http://stackoverflow.com/questions/8986593/how-to-customize-parameter-names-when-binding-spring-mvc-command-objects
【2】http://geekabyte.blogspot.tw/2014/08/how-to-inject-objects-into-spring-mvc.html
【3】http://www.cnblogs.com/daxin/p/3296493.html
微信公众号: 架构师日常笔记 欢迎关注!