首页 > 代码库 > spring mvc踩坑记

spring mvc踩坑记

前言

主要介绍自己在学习spring mvc过程中踩到的一些坑,涉及到当时遇到这个错误是如何思考的,对思路进行总结,下次遇到类似的错误能够提供一些思路甚至快速解决。

环境准备

jdk8,spring4.3.3.RELEASE,spring mvc与spring版本一致,maven3.2.5,tomcat7

目标:

1.测试spring mvc的json参数绑定功能

2.测试spring mvc的返回json功能

项目目录(已经推送到github:https://github.com/ComingOnuguys/frankwin):

技术分享

测试1相关代码:
index.jsp

 1 <%@ page language="java" contentType="text/html; charset=utf-8"
 2     pageEncoding="utf-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 7 <title>Insert title here</title>
 8 <script type="text/javascript"
 9     src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
10 </head>
11 <body>
12     <div>
13         id:<input type="text" id="id" value="http://www.mamicode.com/123456"><br>
14         menuName:<input type="text" id="menuName" value="http://www.mamicode.com/李白"><br>
15         <input type="button" id="abc" value="http://www.mamicode.com/test">
16     </div>
17 
18     <script type="text/javascript">
19         window.onload = function() {
20             $("#abc").click(function() {
21                 var menu = {
22                     "id" : $("#id").val(),
23                     "menuName" : $("#menuName").val()
24                 };
25                 var jsonStr = JSON.stringify(menu);
26                 $.ajax({
27                     type : "POST",
28                     async : false,
29                     url : "http://localhost:8080/ssm/getList",
30                     dataType : "json",
31                     contentType : "application/json;charset=UTF-8",
32                     data : jsonStr,
33                     success : function(data) {
34                         alert("123");
35                     }
36                 });
37             });
38         }
39     </script>
40 </body>
41 </html>


getList.jsp(为了返回的时候不报404的错)

 1 <%@ page language="java" contentType="text/html; charset=utf-8"
 2     pageEncoding="utf-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 7 <title>Insert title here</title>
 8 <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
 9 </head>
10 <body>
11 
12     <h2>陈公告</h2>
13     
14 </body>
15 </html>


TestSpringMvcController.java

 1 package com.dg.action;
 2 
 3 import org.springframework.stereotype.Controller;
 4 import org.springframework.web.bind.annotation.RequestBody;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 
 7 import com.dg.bean.Menu;
 8 
 9 @Controller
10 public class TestSpringMvcController {
11 
12     @RequestMapping("getList")
13     public void getList(@RequestBody Menu menu){
14         System.out.println("menu:" + menu);
15     }
16 }


Menu.java

 1 package com.dg.bean;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Menu implements Serializable {
 6 
 7     private static final long serialVersionUID = 410202860691610816L;
 8     private String id;
 9     private String menuCode;
10     private String menuName;
11     private String menuUrl;
12     private String parentMenuId;
13 
14     public String getId() {
15         return id;
16     }
17 
18     public void setId(String id) {
19         this.id = id;
20     }
21 
22     public String getMenuCode() {
23         return menuCode;
24     }
25 
26     public void setMenuCode(String menuCode) {
27         this.menuCode = menuCode;
28     }
29 
30     public String getMenuName() {
31         return menuName;
32     }
33 
34     public void setMenuName(String menuName) {
35         this.menuName = menuName;
36     }
37 
38     public String getMenuUrl() {
39         return menuUrl;
40     }
41 
42     public void setMenuUrl(String menuUrl) {
43         this.menuUrl = menuUrl;
44     }
45 
46     public String getParentMenuId() {
47         return parentMenuId;
48     }
49 
50     public void setParentMenuId(String parentMenuId) {
51         this.parentMenuId = parentMenuId;
52     }
53 
54     @Override
55     public String toString() {
56         return "Menu [id=" + id + ", menuCode=" + menuCode + ", menuName=" + menuName + ", menuUrl=" + menuUrl
57                 + ", parentMenuId=" + parentMenuId + "]";
58     }
59 }

spring-config.xml(进行了最大程度精简)

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
5 
6     
7 </beans>

 

spring-mvc.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:mvc="http://www.springframework.org/schema/mvc" 
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans    
 7                             http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
 8                             http://www.springframework.org/schema/context    
 9                             http://www.springframework.org/schema/context/spring-context-4.0.xsd    
10                             http://www.springframework.org/schema/mvc
11                             http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
12                             ">
13     <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 -->
14     <context:component-scan base-package="com.dg.action" />
15 
16     <!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
17     <mvc:annotation-driven/>
18     
19     <!-- 定义跳转的文件的前后缀 ,视图模式配置 -->
20      <bean
21         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
22         <property name="prefix" value="http://www.mamicode.com/WEB-INF/jsp/" />
23         <property name="suffix" value="http://www.mamicode.com/.jsp" />
24     </bean> 
25 
26 
27 </beans> 

web.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://java.sun.com/xml/ns/javaee"
 4     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 5     version="3.0">
 6     <display-name>ssm</display-name>
 7     <!-- Spring和mybatis的配置文件 -->
 8     <context-param>
 9         <param-name>contextConfigLocation</param-name>
10         <param-value>classpath:spring-config.xml</param-value>
11     </context-param>
12     <!-- 编码过滤器 -->
13     <filter>
14         <filter-name>encodingFilter</filter-name>
15         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
16         <async-supported>true</async-supported>
17         <init-param>
18             <param-name>encoding</param-name>
19             <param-value>UTF-8</param-value>
20         </init-param>
21     </filter>
22     <filter-mapping>
23         <filter-name>encodingFilter</filter-name>
24         <url-pattern>/*</url-pattern>
25     </filter-mapping>
26     <!-- Spring监听器 -->
27     <listener>
28         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
29     </listener>
30     <!-- 防止Spring内存溢出监听器 -->
31     <listener>
32         <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
33     </listener>
34 
35     <!-- Spring MVC servlet -->
36     <servlet>
37         <servlet-name>SpringMVC</servlet-name>
38         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
39         <init-param>
40             <param-name>contextConfigLocation</param-name>
41             <param-value>classpath:spring-mvc.xml</param-value>
42         </init-param>
43         <load-on-startup>1</load-on-startup>
44         <async-supported>true</async-supported>
45     </servlet>
46     <servlet-mapping>
47         <servlet-name>SpringMVC</servlet-name>
48         <!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
49         <url-pattern>/</url-pattern>
50     </servlet-mapping>
51     <!-- 设置静态资源映射 -->
52     <servlet-mapping>
53         <servlet-name>default</servlet-name>
54         <url-pattern>*.jpg</url-pattern>
55     </servlet-mapping>
56     <servlet-mapping>
57         <servlet-name>default</servlet-name>
58         <url-pattern>*.gif</url-pattern>
59     </servlet-mapping>
60     
61     <servlet-mapping>
62         <servlet-name>default</servlet-name>
63         <url-pattern>*.png</url-pattern>
64     </servlet-mapping>
65     
66     <servlet-mapping>
67         <servlet-name>default</servlet-name>
68         <url-pattern>*.js</url-pattern>
69     </servlet-mapping>
70     <servlet-mapping>
71         <servlet-name>default</servlet-name>
72         <url-pattern>*.css</url-pattern>
73     </servlet-mapping>
74     <welcome-file-list>
75         <welcome-file>/index.jsp</welcome-file>
76     </welcome-file-list>
77 
78 </web-app> 

pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 4 
 5     <modelVersion>4.0.0</modelVersion>
 6     <packaging>war</packaging>
 7 
 8     <name>ssm</name>
 9     <groupId>dg</groupId>
10     <artifactId>ssm</artifactId>
11     <version>0.0.1-SNAPSHOT</version>
12 
13     <properties>
14         <!-- spring版本号 -->
15         <spring.version>4.3.3.RELEASE</spring.version>
16         <!-- log4j日志文件管理包版本 -->
17         <slf4j.version>1.7.7</slf4j.version>
18         <log4j.version>1.2.17</log4j.version>
19     </properties>
20 
21     <dependencies>
22         <!-- spring核心包 -->
23         <dependency>
24             <groupId>org.springframework</groupId>
25             <artifactId>spring-core</artifactId>
26             <version>${spring.version}</version>
27         </dependency>
28         <dependency>
29             <groupId>org.springframework</groupId>
30             <artifactId>spring-web</artifactId>
31             <version>${spring.version}</version>
32         </dependency>
33         <dependency>
34             <groupId>org.springframework</groupId>
35             <artifactId>spring-webmvc</artifactId>
36             <version>${spring.version}</version>
37         </dependency>
38         <!-- 日志文件管理包 -->
39         <!-- log start -->
40         <dependency>
41             <groupId>log4j</groupId>
42             <artifactId>log4j</artifactId>
43             <version>${log4j.version}</version>
44         </dependency>
45         <dependency>
46             <groupId>org.codehaus.jackson</groupId>
47             <artifactId>jackson-mapper-asl</artifactId>
48             <version>1.9.13</version>
49         </dependency>
50     
51     </dependencies>
52 </project>

 

坑一:spring mvc的json包
对于测试1,采用以上代码一直返回httpcode415 (Unsupported Media Type)。如下图:

技术分享

后台报错的日志为:

 1 [org.springframework.web.servlet.DispatcherServlet] - DispatcherServlet with name ‘SpringMVC‘ processing POST request for [/ssm/getList]
 2   [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] - Looking up handler method for path /getList
 3   [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] - Returning handler method [public void com.dg.action.TestSpringMvcController.getList(com.dg.bean.Menu)]
 4   [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean ‘testSpringMvcController‘
 5   [org.springframework.web.cors.DefaultCorsProcessor] - Skip CORS processing: request is from same origin
 6   [org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod] - Error resolving argument [0] [type=com.dg.bean.Menu]
 7 HandlerMethod details: 
 8 Controller [com.dg.action.TestSpringMvcController]
 9 Method [public void com.dg.action.TestSpringMvcController.getList(com.dg.bean.Menu)]
10 
11   org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json;charset=UTF-8‘ not supported
12     at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:235)
13     at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:149)
14     at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:127)
15     at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
16     at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
17     at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
18     at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
19     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
20     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
21     at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
22     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
23     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
24     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
25     at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
26     at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
27     at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
28     at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
29     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
30     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
31     at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
32     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
33     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
34     at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
35     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
36     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
37     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
38     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
39     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
40     at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
41     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
42     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
43     at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
44     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
45     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
46     at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074)
47     at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
48     at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
49     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
50     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
51     at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
52     at java.lang.Thread.run(Thread.java:745)
53 [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver] - Resolving exception from handler [public void com.dg.action.TestSpringMvcController.getList(com.dg.bean.Menu)]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json;charset=UTF-8‘ not supported
54   [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver] - Resolving exception from handler [public void com.dg.action.TestSpringMvcController.getList(com.dg.bean.Menu)]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json;charset=UTF-8‘ not supported
55   [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] - Resolving exception from handler [public void com.dg.action.TestSpringMvcController.getList(com.dg.bean.Menu)]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json;charset=UTF-8‘ not supported
56   [org.springframework.web.servlet.DispatcherServlet] - Null ModelAndView returned to DispatcherServlet with name ‘SpringMVC‘: assuming HandlerAdapter completed request handling
57   [org.springframework.web.servlet.DispatcherServlet] - Successfully completed request
58   

 

看到这个报错提示的第一反应是把前台请求index.jsp的contentType这一行注释掉(注:不写默认值是application/x-www-form-urlencoded;charset=UTF-8):
//contentType : "application/json;charset=UTF-8",
报错变成了:Content type ‘application/x-www-form-urlencoded;charset=UTF-8‘ not supported
都不支持,这就没法玩了。考虑controller的参数menu不加@RequestBody能不能参数绑定成功,于是去掉@RequestBody,前台请求注释contentType这一行注释掉,发送的参数由jsonStr这个变量改为menu,发现可以正常注入,根据之前学到的知识,String类型和Json的参数绑定是分别由StringHttpMessageConverter和MappingJackson2HttpMessageConverter进行处理,于是把断点分别打在两个类的构造方法上,断点情况如图:

技术分享

启动tomcat,发现只进到了StringHttpMessageConverter这个类的构造函数里面。报错的原因找到了(没有加载MappingJackson2HttpMessageConverter),但是为什么没有加载,还不知道原因。重新启动tomcat,顺着StringHttpMessageConverter执行下来,发现有这么一段,

技术分享

看到MappingJackson2HttpMessageConverter非常眼熟,但是很遗憾,这里的jackson2present变量值是false,

技术分享

看到这里,接着就是去找该变量是false的原因了。Ctrl+Shift+K找到声明这个变量的地方,

技术分享

选中com.fasterxml.jackson.databind.ObjectMapper,Ctrl+shift+T,没有找到这个类。嗯,问题就处在这了。意思到有可能有什么包没有加进来。查阅资料得知spring mvc从3.2版本开始依赖的json包由原先的jackson-mapper-asl.jar、jackson-core-asl-1.9.13.jar变更为了jackson-core.jar、jackson-dataformat-xml.jar

技术分享

于是到mavenrepository找到springmvc4.3.3.RELEASE依赖的包的列表,上面的结论得到验证。

技术分享

接着修改maven依赖json的包,再使用上面的那些代码,终于大功告成~

 技术分享

技术分享

 

总结:


 

1、ajax请求如果不写contentType,则默认是application/x-www-form-urlencoded;charset=UTF-8

2、@RequestBody的作用是将请求参数的json串绑定到实体类上,要实现这个,请求的contentType要设置成contentType : "application/json;charset=UTF-8"

3、更明确spring mvc的这些默认的处理类在tomcat容器启动时就进行初始化。

4、当怀疑是包依赖产生的问题时,可以到mavenrepository找到该框架依赖的jar包。

 

包依赖的问题解决后,测试2也成功实现目标。

 (完)

 

spring mvc踩坑记