springmvc 4.3,RequestParamMethodArgumentResolver无法正常解析String参数问题解决

搭建一个新工程时,想使用最新稳当版的springmvc,所以选择了最新的版本

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.3.1.RELEASE</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>

  

然后用以往的经验写了一个极其简单的Controller方法

    @ResponseBody
    @RequestMapping(value = "/cates")
    public Object cates(@PathVariable Long pageId, @PathVariable Long moduleId,
                        String goodIds, Goods goods) {
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("1","1");
        return map;
    }

  

然而,奇怪的事情发生了。

参入参数:http://localhost:8088/1/2/cates.json?goodIds=2,3

运行时goodIds始终为null,而如果用bean属性的方式解析则正常,即goods.goodIds是可以正常解析到的。

我首先想到的是,给goodIds加上RequestParam注解。

    
@ResponseBody
    @RequestMapping(value = "/cates")
    public Object cates(@PathVariable Long pageId, @PathVariable Long moduleId,
                        @RequestParam(value = "goodIds", required = false) String goodIds, Goods goods) {
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("1","1");
        return map;
    }

  

这样试了一下,还是不行,网上简单的搜了一下资料,并没有搜到这种情况,只能打断点看看问题出在哪里。

由于spring-mvc.xml里面,我写死了使用RequestMappingHandlerAdapter来处理方法级的相关操作。进入这个类,有一个spring的初始化对象接口的实现

@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

  

可以清晰的看见getDefaultArgumentResolvers()方法就是默认的参数解析器。

/**
	 * Return the list of argument resolvers to use including built-in resolvers
	 * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
	 */
	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		....

  

这里就不截该方法的全部代码了,我们把关注点集中到

RequestParamMethodArgumentResolver

这个解析器,由于代码层级太深,有兴趣的同学可以自己打断点一步一步的走,总之,最后可以走到这里来MultipartResolutionDelegate。

isPartCollection是用来判断参数是否为进行文件上传相关的方法参数

private static boolean isPartCollection(MethodParameter methodParam) {
		return (servletPartClass == getCollectionParameterType(methodParam));
	}
	private static Class<?> getCollectionParameterType(MethodParameter methodParam) {
		Class<?> paramType = methodParam.getNestedParameterType();
		if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
			Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
			if (valueType != null) {
				return valueType;
			}
		}
		return null;
	}

  

由于goodIds是String对象,调用getCollectionParameterType方法后,返回的无疑是null,而最终isPartCollection方法返回的竟然是true,这点无疑是我们无法接受的。细磕原因,我们会发现servletPartClass值为null,因此返回了true。

再看它的初始化方式

/**
 * A common delegate for {@code HandlerMethodArgumentResolver} implementations
 * which need to resolve {@link MultipartFile} and {@link Part} arguments.
 *
 * @author Juergen Hoeller
 * @since 4.3
 */
public abstract class MultipartResolutionDelegate {

	public static final Object UNRESOLVABLE = new Object();

	private static Class<?> servletPartClass = null;

	static {
		try {
			servletPartClass = ClassUtils.forName("javax.servlet.http.Part",
					MultipartResolutionDelegate.class.getClassLoader());
		}
		catch (ClassNotFoundException ex) {
			// Servlet 3.0 javax.servlet.http.Part type not available -
			// Part references simply not supported then.
		}
	}

  

需要指出的是,MultipartResolutionDelegate是spring-web在4.3版本才出现的,因此在某些低版本的springmvc下,我们出现的问题不是一定能重现的,具体看框架实现的方式。

从这里可以看到servletPartClass是javax.servlet.http.Part的类型对象。同时catch块也分明注释了

javax.servlet.http.Part是serlvet 3.0才支持的对象,因此要想servletPartClass不为null,应该使用serlvet3.0的版本依赖,因此我头脑一热,马上吧serlvet-api从2.5升级到了3.0.1

     
  <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
                <scope>provided</scope>
            </dependency>

  

     

现在,在ide里面,能搜索到javax.servlet.http.Part这个类了,火速重启工程,然而,servletPartClass依然是null,坚强的null。

旋即,我才想起,启动工程用的是tomcat6,火速想到tomcat6有可能不支持serlvet3.0规范。

tomcat官网http://tomcat.apache.org/whichversion.html页的tomcat7描述中,有这么一段话

Apache Tomcat 7.x
Apache Tomcat 7.x builds upon the improvements made in
Tomcat 6.0.x and implements the Servlet 3.0,JSP 2.2, EL 2.2 andWeb Socket 1.1 specifications. In addition
to that, it includes the following improvements:
Web application memory leak detection and prevention
Improved security for the Manager and Host Manager applications
Generic CSRF protection
Support for including external content directly in a web application
Refactoring (connectors, lifecycle) and lots of internal code clean-up
Apache Tomcat 6.x
Apache Tomcat 6.x builds upon the improvements made in
Tomcat 5.5.x and implements the Servlet 2.5 andJSP 2.1 specifications. In addition to that, it includes the
following improvements:

  

tomcat7.x支持serlvet3.0规范,而tomcat6并不支持,火速下载tomcat7,应用后,servletPartClass不在为null,终于哦了,成功获取到goodIds

总结:

1.springmvc 4.3版本新增的很多类,如MultipartResolutionDelegate,他们是基于servlet 3.0规范实现的,如果使用旧的serlvet2.5规范,是无法正常工作的,同时,tomcat也需要同时支持serlvet3.0,最低版本的tomcat也需要是7.x

2.其实在看spring mvc 4.3的maven版本依赖时,里面就有这块依赖

 <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

人家也不是写着玩的,这意味着,要想用spring mvc 4.3,那么你就应该使用serlvet3.0规范,tomcat至少使用7.x

3.MultipartResolutionDelegate本身是一个抽象类,用来解析判断MultipartFile和Part类型的方法参数的,如果无法获取到Part类型对象,则无法正确执行它应有的功能,在使用新的工具或版本时,难免会出现无法理解的事,找到原因,分析解决后会有一种豁然开朗的感觉

时间: 2024-10-29 19:12:27

springmvc 4.3,RequestParamMethodArgumentResolver无法正常解析String参数问题解决的相关文章

springMVC框架下JQuery传递并解析Json数据

json作为一种轻量级的数据交换格式,在前后台数据交换中占领着很重要的地位.Json的语法很简单,採用的是键值对表示形式.JSON 能够将 JavaScript 对象中表示的一组数据转换为字符串,然后就能够在函数之间轻松地传递这个字符串,或者在异步应用程序中将字符串从 Web 客户机传递给server端程序,也能够从server端程序传递json格式的字符串给前端并由前端解释.这个字符串是符合json语法的,而json语法又是javascript语法的子集,所以javascript很easy解释

SpringMVC系列(七)视图解析器和视图

在springmvc.xml里面配置视图解析器 1 <!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 --> 2 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 3 <property name="prefix" value="/WEB-INF/views/">&l

eclipse中导入jdk源码、SpringMVC注解@RequestParam、SpringMVC文件上传源码解析、ajax上传excel文件

eclipse中导入jdk源码:http://blog.csdn.net/evolly/article/details/18403321, http://www.codingwhy.com/view/799.html. ------------------------------- SpringMVC注解@RequestParam:http://825635381.iteye.com/blog/2196911. --------------------------- SpringMVC文件上传源

深入解析String#intern

转自:https://tech.meituan.com/in_depth_understanding_string_intern.html 深入解析String#intern john_yang ·2014-03-06 17:10 引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常量池就类似一个JAVA系统级别提供的缓存. 8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊.

在JAVA中 解析String,形如(0&lt;p&lt;10 or 0&lt;=p&lt;10 or 0&lt;p&lt;=10 or 0&lt;=p&lt;=10)的字符串,看所传入的参数是否在表达式内

今天在项目中需要解析一个参数范围的字符串,然后判断一个传入参数是否在这个表达式内.因为参数范围字符串是(0<p<10 or 0<=p<10 or 0<p<=10 or 0<=p<=10)的组合,所以我觉得直接去进行split处理比较麻烦,就运用了String转为 byte[] 再做处理.代码如下. 不知道大家有没有更好的处理方式. package test_lzg; import java.util.ArrayList; import java.util.L

SpringMVC控制器接收不了PUT提交的参数的解决方案

摘要: SpringMVC控制器接收不了PUT提交的参数的解决方案 这次改造了下框架,把控制器的API全部REST化,不做不知道,SpringMVC的REST有各种坑让你去跳,顺利绕过它们花了我不少时间,这次来提下SpringMVC的PUT提交参数为null的情况. 照常先贴出我的控制器代码,没什么特别的,就是打印出接受到的前台参数值: @RequestMapping(value = "/{id}", method = RequestMethod.PUT) @ResponseBody

Scala正则和抽取器:解析方法参数

在<正则表达式基础知识>中概括了正则表达式的基础知识, 本文讲解如何使用正则表达式解析方法参数,从而可以根据 DAO 自动生成 Service. 在做 Java 项目时,常常要根据 DAO 生成 Service , 而 Service 有时是简单的调用 DAO 方法.比如根据 public CreativeDO findByCreativeId(Long creativeId)  生成如下代码: public CreativeDO findByCreativeId(Long creativeI

正则表达式解析url参数

解析url参数正则:(?<=\?|&)[\w\={},:''""]*(?<=[^#]) 正好项目中要用到 捣鼓了好久还是不会.最终放弃使用split分割的方式解析发现好落伍 public static NameValueCollection QueryString(string path) { if (string.IsNullOrEmpty(path)) { return null; } var arr=path.Split(new char[] { '?' },

Java switch 语句使用 String 参数

原文同步至 http://www.waylau.com/java-switch-use-string/ 当我尝试在 switch 语句使用 String 参数时(注意ctrType为字符串) switch (ctrType) { case "01" : exceptionType = "读FC参数数据"; break; case "03" : exceptionType = "读FC保存的当前表计数据"; break; def