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="123456"><br> 14 menuName:<input type="text" id="menuName" value="李白"><br> 15 <input type="button" id="abc" value="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="/WEB-INF/jsp/" /> 23 <property name="suffix" value=".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也成功实现目标。
(完)