博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC自动封装List对象 —— 自定义参数解析器
阅读量:4952 次
发布时间:2019-06-11

本文共 8588 字,大约阅读时间需要 28 分钟。

  前台传递的参数为集合对象时,后台Controller希望用一个List集合接收数据。

  原生SpringMVC是不支持,Controller参数定义为List类型时,接收参数会报如下错误:

org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.List]: Specified class is an interfaceat org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:99) ~[spring-beans-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:139) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:82) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:106) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]

  查看了一下源码,发现问题在于ModelAttributeMethodProcessor解析参数时,会先使用BeanUtils.instantiateClass方法创建一个对象实例来接收参数。然而List是一个接口,不能被实例化。于是我想到,既然自带的参数解析器不能解析,那就自定义一个参数解析器来实现这个功能。

  List存在类型擦除,在运行期不能够通过反射来获取泛型,所以得有个办法获取泛型,我便定义了一个注解ListParam

/** * 强制申明List的泛型
* 用于反射获取参数类型 * * @author zengyuanjun * */@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ListParam { public Class
value();}

  接下来写自定义的解析器ListArgumentResolver 。解析器需要实现HandlerMethodArgumentResolver接口,该接口有两个方法:

  1. supportsParameter(MethodParameter parameter) 返回当前解析器是否支持该参数。

  2. resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) 具体的参数解析实现。

  先使用webRequest.getParameterValues(String paramName)获取request中的参数数组,遍历添加到List<MutablePropertyValues>中,以分配给各个对象。再循环使用ServletRequestDataBinder绑定PropertyValues进行类型转换,得到需要的对象集合。

import java.lang.reflect.Field;import java.util.ArrayList;import java.util.List;import org.springframework.beans.BeanUtils;import org.springframework.beans.MutablePropertyValues;import org.springframework.core.MethodParameter;import org.springframework.stereotype.Component;import org.springframework.util.ClassUtils;import org.springframework.web.bind.ServletRequestDataBinder;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.support.WebDataBinderFactory;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.method.support.ModelAndViewContainer;import com.zyj.springboot.study.annotation.ListParam;/** * List集合参数解析器 
* @author zengyuanjun * */@Componentpublic class ListArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { if (null != parameter.getParameterAnnotation(ListParam.class) && List.class.equals(parameter.getParameterType())) { return true; } return false; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String[] parameterValues = null; MutablePropertyValues mutablePropertyValues = null; Class
elementClass = getElementTypeFromAnnotation(parameter); List
mpvList = new ArrayList
(); Field[] fields = elementClass.getDeclaredFields(); for (Field field : fields) { parameterValues = webRequest.getParameterValues(field.getName()); if (null == parameterValues) { continue; } for (int i = 0; i < parameterValues.length; i++) { if (mpvList.size() <= i) { mutablePropertyValues = new MutablePropertyValues(); mutablePropertyValues.add(field.getName(), parameterValues[i]); mpvList.add(mutablePropertyValues); } else { mutablePropertyValues = mpvList.get(i); mutablePropertyValues.add(field.getName(), parameterValues[i]); } } } String name = ClassUtils.getShortNameAsProperty(elementClass); Object attribute = null; WebDataBinder binder = null; ServletRequestDataBinder servletBinder = null; Object element = null; List
actualParameter = new ArrayList(mpvList.size()); for (int i = 0; i < mpvList.size(); i++) { attribute = BeanUtils.instantiateClass(elementClass); binder = binderFactory.createBinder(webRequest, attribute, name); servletBinder = (ServletRequestDataBinder) binder; servletBinder.bind(mpvList.get(i)); element = binder.getTarget(); actualParameter.add(binder.convertIfNecessary(element, elementClass, parameter)); } return actualParameter; } private Class
getElementTypeFromAnnotation(MethodParameter parameter) { ListParam parameterAnnotation = parameter.getParameterAnnotation(ListParam.class); return parameterAnnotation.value(); }}

  定义好解析器后,需要注入到RequestMappingHandlerAdapter中,这里要注意,自带的ServletModelAttributeMethodProcessor解析器是对List类型生效的!!!所以必须把自定义的解析器放到ServletModelAttributeMethodProcessor前面,我这里直接把自定义的解析器ListArgumentResolver放到了第一个。

import java.util.LinkedList;import java.util.List;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import com.zyj.springboot.study.resolver.ListArgumentResolver;/** * 初始化将自定义的ListArgumentResolver注入到RequestMappingHandlerAdapter中 * @author zengyuanjun * */@Componentpublic class InitialListArgumentResolver implements ApplicationContextAware {    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        RequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);        List
argumentResolvers = handlerAdapter.getArgumentResolvers(); List
resolvers = new LinkedList
(); resolvers.add(new ListArgumentResolver()); resolvers.addAll(argumentResolvers); handlerAdapter.setArgumentResolvers(resolvers); }}

  然后?就没有然后了,使用时在List参数前加上ListParam注解,申明一下类型就好。最后还是给个使用的小例子吧!

  页面定义了多个user对象的信息,它们的name是重复的。

(-.-) ZYJ (*-*)

  后台controller使用List<User>接收,注意要加上@ListParam(User.class)申明类型。

@RestControllerpublic class UserContoller {    @Autowired    private UserService userService;        @RequestMapping("/addUsers")    public Object addUsers(@ListParam(User.class) List
users){ return userService.addUsers(users); }}

 

转载于:https://www.cnblogs.com/zengyuanjun/p/9900350.html

你可能感兴趣的文章
点击表格的单元格时实现变颜色,通过for循环为每个单元格添加一个onclick事件...
查看>>
webform Response的一些成员
查看>>
Countries in War(强连通分量及其缩点)
查看>>
Eclipse中用Link方式安装Maven插件(转载)
查看>>
Android菜鸟的成长笔记(11)——Android中的事件处理
查看>>
JStrom的zk数据
查看>>
使用“dotconnect for oracle”绕过oracle客户端连接Oracle数据库
查看>>
CentOS/RHEL Linux安装EPEL第三方软件源
查看>>
redisson
查看>>
Weblogic集群
查看>>
HDU 5351 MZL's Border (多校联合第5场1009)
查看>>
js三种定义类的方法
查看>>
离线批量数据通道Tunnel的最佳实践及常见问题
查看>>
Struts2学习第2天--Struts2的Servlet的API的访问 Struts2的结果页面的配置 Struts2的数据的封装(包括复杂类型)...
查看>>
python3练习100题——044
查看>>
ORACLE1.5(上班前必须掌握-开项目的准备)
查看>>
正则表达式学习笔记(2)元字符和修饰符
查看>>
基础算法(四)——深度优先搜索
查看>>
PHP 内存泄漏分析定位(转载)
查看>>
Linux基础命令之文件过滤及内容编辑处理(一)
查看>>