您的位置:首页 > 编程语言 > Java开发

SpringMVC的Controller接口方法参数解析

2017-09-22 16:59 323 查看
一、举例说明

(1)示例:方法参数没有任何注解 public Object query(List<Long> idList),传递参数为 .param("idList", "1").param("idList", "2")

结果:失败。org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [java.util.List]: Specified class is an interface

原因:没有任何注解并且不属于原生类型以及在业务系统不常见的类型,常用的List,Set就不满足前面的条件,所以参数的解析就交给了保底的ServletModelAttributeMethodProcessor以及它的父类ModelAttributeMethodProcessor,进过一系列的处理到达BeanUtils.instantiateClass(parameter.getParameterType())函数进行参数类型初始化。

(2)示例:方法参数没有任何注解 public Object query(ArrayList<Long> idList) ,传递参数为 .param("idList", "1").param("idList", "2")

结果:失败。内部报错throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,matches.buildErrorMessage(), matches.getPossibleMatches()),SpringMVC隐藏了错误,结果虽然没有报错,但是参数也没有映射成功。

原因:同(1),区别是ArrayList可以初始化,但是参数具体映射需要根据set/get属性方法进行反射赋值,ArrayList并没有set属性方法,所以失败。

(3)示例:方法参数有注解 public Object query(@RequestParam("idList") List<Long> idList) ,传递参数为 .param("idList", "1").param("idList", "2")

结果:成功

原因:因为我们有@RequestParam注解,所以参数解析给了优先级最高的RequestParamMethodArgumentResolver,它不需要对参数类型进行初始化,

而是采用另一套解析逻辑(它的父类AbstractNamedValueMethodArgumentResolver)来处理,在这里先针对注解进行解析,获取参数名value,通过String[] paramValues = webRequest.getParameterValues(name)获取参数值,然后选择SpringMVC容器预先加载的100多个转换器中的StringToCollectionConverter进行参数转化,不经过set/get属性方法,根据需要的参数类型List直接创建ArrayList,然后赋值成功。

(4)示例:方法参数有注解 public Object query(@RequestParam("idList") List<Long> notIdList) ,传递参数为 .param("idList", "1").param("idList", "2")

结果:成功

原因:当有注解时不会关注接口的参数名,只会以注解里面的value为准

(5)示例:方法参数没有任何注解 public Object query(User user) ,传递参数为 .param("name", "1").param("age", "2").param("time", "2017-09-22 00:00:00"),User对象里面含有这三个属性。

结果:成功

原因:同(1),区别是该类的两个属性具有set/get属性方法,writeMethod.invoke(this.object, value)可以赋值成功,在反射之前,需要对参数值进行类型转换Converter,因为字符串转日期的转换器容器自身并没有,所以我们需要自定义,通过@InitBinder注解定义一个转换器,容器启动就会加载到转换器列表里,用的时候会取出来使用。

(6)示例:方法参数没有任何注解 public Object query(User user) ,传递参数为.param("idList", "1").param("idList", "2"),User对象里面含有这个List集合属性。

结果:成功

原因:同(5),获取传递来的参数Enumeration<String> paramNames = request.getParameterNames(),同过set方法反射赋值

二、总结

1、SpringMVC其实是鼓励我们对参数使用注解的,容器会预先加载几十个Resolver到list集合里面,在进行参数解析的时候侯从中按顺序选择,RequestParamMethodArgumentResolver是排在第一个的,而不用注解的话会使用ServletModelAttributeMethodProcessor进行解析,它在集合的位置是最后,所以每个参数需要遍历一遍,但是容器会对每次参数的解析Resolver关系进行缓存,效率也没有多少降低。

<
4000
p style="margin-top:0px;margin-bottom:0px;padding-top:0px;padding-bottom:0px;color:rgb(85,85,85);font-family:'microsoft yahei';font-size:15px;">
2、集合参数传递,容器内部支持以逗号的集合参数param("idList", "1,2,3,4")

3、ServletModelAttributeMethodProcessor会先给变量类进行实例化,再根据set/get反射赋值

4、自定义对象User最好使用@ModelAttribute 注解,其他的基本类型包装对象以及集合使用@RequestParam注解,虽然@RequestParam的required方法经常会打印出很多错误日志,RESTful API接口可以使用@PathVariable注解。

三、补充

根据前面的例子显示,集合类参数不使用注解的话就不能成功被解析,这怎么能忍?下面是我参考源码改写的一个支持集合的CollectionArgumentResolver

public class CollectionArgumentResolver extends AbstractNamedValueMethodArgumentResolver

        implements UriComponentsContributor {

    public CollectionArgumentResolver() {

        super();

    }

    @Override

    protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {

        return  new RequestParamNamedValueInfo();

    }

    @Override

    public boolean supportsParameter(MethodParameter parameter) {

        if (parameter.hasParameterAnnotations()) {

            return false;

        }

        Class<?> paramType = parameter.getParameterType();

        return Collection.class.isAssignableFrom(paramType);

    }

    @Override

    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {

        Object arg;

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);

        MultipartHttpServletRequest multipartRequest =

                WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);

        if (MultipartFile.class.equals(parameter.getParameterType())) {

            assertIsMultipartRequest(servletRequest);

            Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");

            arg = multipartRequest.getFile(name);

        }

        else if (isMultipartFileCollection(parameter)) {

            assertIsMultipartRequest(servletRequest);

            Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");

            arg = multipartRequest.getFiles(name);

        }

        else if(isMultipartFileArray(parameter)) {

            assertIsMultipartRequest(servletRequest);

            Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");

            arg = multipartRequest.getFiles(name).toArray(new MultipartFile[0]);

        }

        else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {

            assertIsMultipartRequest(servletRequest);

            arg = servletRequest.getPart(name);

        }

        else if (isPartCollection(parameter)) {

            assertIsMultipartRequest(servletRequest);

            arg = new ArrayList<Object>(servletRequest.getParts());

        }

        else if (isPartArray(parameter)) {

            assertIsMultipartRequest(servletRequest);

            arg = RequestPartResolver.resolvePart(servletRequest);

        }

        else {

            arg = null;

            if (multipartRequest != null) {

                List<MultipartFile> files = multipartRequest.getFiles(name);

                if (!files.isEmpty()) {

                    arg = (files.size() == 1 ? files.get(0) : files);

                }

            }

            if (arg == null) {

                String[] paramValues = webRequest.getParameterValues(name);

                if (paramValues != null) {

                    arg = paramValues.length == 1 ? paramValues[0] : paramValues;

                }

            }

        }

        return arg;

    }

    private void assertIsMultipartRequest(HttpServletRequest request) {

        String contentType = request.getContentType();

        if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {

            throw new MultipartException("The current request is not a multipart request");

        }

    }

    private boolean isMultipartFileCollection(MethodParameter parameter) {

        Class<?> collectionType = getCollectionParameterType(parameter);

        return ((collectionType != null) && collectionType.equals(MultipartFile.class));

    }

    private boolean isPartCollection(MethodParameter parameter) {

        Class<?> collectionType = getCollectionParameterType(parameter);

        return ((collectionType != null) && "javax.servlet.http.Part".equals(collectionType.getName()));

    }

    private boolean isPartArray(MethodParameter parameter) {

        Class<?> paramType = parameter.getParameterType().getComponentType();

        return ((paramType != null) && "javax.servlet.http.Part".equals(paramType.getName()));

    }

    private boolean isMultipartFileArray(MethodParameter parameter) {

        Class<?> paramType = parameter.getParameterType().getComponentType();

        return ((paramType != null) && MultipartFile.class.equals(paramType));

    }

    private Class<?> getCollectionParameterType(MethodParameter parameter) {

        Class<?> paramType = parameter.getParameterType();

        if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){

            Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(parameter);

            if (valueType != null) {

                return valueType;

            }

        }

        return null;

    }

    @Override

    protected void handleMissingValue(String paramName, MethodParameter parameter) throws ServletException {

        throw new MissingServletRequestParameterException(paramName, parameter.getParameterType().getSimpleName());

    }

    @Override

    public void contributeMethodArgument(MethodParameter parameter, Object value,

                                         UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {

    }

    private static class RequestParamNamedValueInfo extends NamedValueInfo {

        public RequestParamNamedValueInfo() {

            super("", false, ValueConstants.DEFAULT_NONE);

        }

    }

    private static class RequestPartResolver {

        public static Object resolvePart(HttpServletRequest servletRequest) throws Exception {

            return servletRequest.getParts().toArray(new Part[servletRequest.getParts().size()]);

        }

    }

}

 <mvc:annotation-driven>

        <mvc:argument-resolvers>

            <bean class="com.ph3636.CollectionArgumentResolver"/>

        </mvc:argument-resolvers>

    </mvc:annotation-driven>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐