JSON数据转换
为了实现浏览器与控制器类(Controller)之间的数据交互,Spring提供了一个HttpMessageConverter<T>接口来完成此项工作。该接口主要用于将请求信息中的数据转换为一个类型为T的对象,并将类型为T的对象绑定到请求方法的参数中,或者将对象转换为响应信息传递给浏览器显示。
Spring为HttpMessageConverter<T>接口提供了很多实现类,这些实现类可以对不同类型的数据进行信息转换。其中MappingJackson2HttpMessageConverter是Spring MVC默认处理JSON格式请求响应的实现类。该实现类利用Jackson开源包读写JSON数据,将Java对象转换为JSON对象和XML文档,同时也可以将JSON对象和XML文档转换为Java对象。
要使用MappingJackson2HttpMessageConverter对数据进行转换,就需要使用Jackson的开源包,开发时所需的开源包及其描述如下所示:
● jackson-annoations-2.8.8.jar:JSON转换注解包;
● jackson-core-2.8.8.jar:JSON转换核心包;
● jackson-databind-2.8.8.jar:JSON转换的数据绑定包。
以上3个Jackson的开源包在本书编写时的最新稳定版本为2.8.8,读者可以通过链接“http://mvnrepository.com/artifact/com.fasterxml.jackson.core
”下载得到,也可以在配套资源的源代码中找到Jackson的开源包。
在使用注解式开发时,需要用到2个重要的JSON格式转换注解,分别为@RequestBody和@ResponseBody,关于这两个注解的说明如表1所示。
表1 JSON数据交互注解及说明
注解 | 说明 |
---|---|
@RequestBody | 用于将请求体中的数据绑定到方法的形参中。该注解用在方法的形参上。 |
@ResponseBody | 用于直接返回return对象。该注解用在方法上。 |
了解了Spring MVC中JSON数据交互需要使用的类和注解后,接下来通过一个案例来演示如何进行JSON数据的交互,具体实现步骤如下。
(1)创建项目并导入相关JAR包。使用Eclipse创建一个名为chapter14的Web项目,然后将Spring MVC相关JAR包、JSON转换包添加到项目的lib目录中,并发布到类路径下。添加后的lib目录如图1所示。
图1 项目相关JAR包
(2)在web.xml中,对Spring MVC的前端控制器等信息进行配置,如文件1所示。
文件1 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://xmlns.jcp.org/xml/ns/javaee"
4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
5 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
6 id="WebApp_ID" version="3.1">
7 <display-name>chapter14</display-name>
8 <welcome-file-list>
9 <welcome-file>index.jsp</welcome-file>
10 </welcome-file-list>
11 <!-- 配置Spring MVC前端控制器 DispatcherServlet -->
12 <servlet>
13 <servlet-name>springmvc</servlet-name>
14 <servlet-class>
15 org.springframework.web.servlet.DispatcherServlet
16 </servlet-class>
17 <!-- 配置Spring MVC加载配置文件路径 -->
18 <init-param>
19 <param-name>contextConfigLocation</param-name>
20 <param-value>classpath:springmvc-config.xml</param-value>
21 </init-param>
22 <!-- 配置服务器启动后立即加载Spring MVC配置文件 -->
23 <load-on-startup>1</load-on-startup>
24 </servlet>
25 <servlet-mapping>
26 <servlet-name>springmvc</servlet-name>
27 <url-pattern>/</url-pattern>
28 </servlet-mapping>
29 </web-app>
(3)在src目录下,创建Spring MVC的核心配置文件springmvc-config.xml,编辑后的代码如文件2所示。
文件2 springmvc-config.xml
1 <beans xmlns="http://www.springframework.org/schema/beans"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:mvc="http://www.springframework.org/schema/mvc"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xmlns:tx="http://www.springframework.org/schema/tx"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
8 http://www.springframework.org/schema/mvc
9 http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
10 http://www.springframework.org/schema/context
11 http://www.springframework.org/schema/context/spring-context-4.3.xsd">
12 <!-- 定义组件扫描器,指定需要扫描的包 -->
13 <context:component-scan base-package="com.itheima.controller" />
14 <!-- 配置注解驱动 -->
15 <mvc:annotation-driven />
16 <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
17 <mvc:resources location="/js/" mapping="/js/**" />
18 <!-- 配置视图解析器 -->
19 <bean class=
20 "org.springframework.web.servlet.view.InternalResourceViewResolver">
21 <property name="prefix" value="/WEB-INF/jsp/" />
22 <property name="suffix" value=".jsp" />
23 </bean>
24 </beans>
在文件2中,不仅配置了组件扫描器和视图解析器,还配置了Spring MVC的注解驱动<mvc:annotation-drivern />和静态资源访问映射<mvc:resources …/>。其中<mvc:annotation-drivern />配置会自动注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter两个Bean,并提供对读写XML和读写JSON等功能的支持。<mvc:resources… />元素用于配置静态资源的访问路径。由于在web.xml中配置的“/”会将页面中引入的静态文件也进行拦截,而拦截后页面中将找不到这些静态资源文件,这样就会引起页面报错。而增加了静态资源的访问映射配置后,程序会自动的去配置路径下找静态的内容。
<mvc:resources …/>中有两个重要属性location和mapping,关于这两个属性的说明如表2所示。
表2 <mvc:resources>标签属性及说明
属性 | 说明 |
---|---|
location | 用于定位需要访问的本地静态资源文件路径,具体到某个文件夹 |
mapping | 匹配静态资源全路径,其中“/**”表示文件夹及其子文件夹下的某个具体文件 |
(4)在src目录下,创建一个com.itheima.po包,并在包中创建一个User类,该类用于封装User类型的请求参数,编辑后如文件3所示。
文件3 User.java
1 package com.itheima.po;
2 /**
3 * 用户POJO
4 */
5 public class User {
6 private String username;
7 private String password;
8 public String getUsername() {
9 return username;
10 }
11 public void setUsername(String username) {
12 this.username = username;
13 }
14 public String getPassword() {
15 return password;
16 }
17 public void setPassword(String password) {
18 this.password = password;
19 }
20 @Override
21 public String toString() {
22 return "User [username=" + username + ", password=" + password + "]";
23 }
24 }
在文件3中,定义了username和password属性,及其对应的getter/setter方法,同时为了方便查询结果重写了toString()方法。
(5)在WebContent目录下,创建页面文件index.jsp来测试JSON数据交互,编辑后如文件4所示。
文件4 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"
4 "http://www.w3.org/TR/html4/loose.dtd">
5 <html>
6 <head>
7 <title>测试JSON交互</title>
8 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
9 <script type="text/javascript"
10 src="${pageContext.request.contextPath }/js/jquery-1.11.3.min.js">
11 </script>
12 <script type="text/javascript">
13 function testJson(){
14 // 获取输入的用户名和密码
15 var username = $("#username").val();
16 var password = $("#password").val();
17 $.ajax({
18 url : "${pageContext.request.contextPath }/testJson",
19 type : "post",
20 // data表示发送的数据
21 data : JSON.stringify({username:username,password:password}),
22 // 定义发送请求的数据格式为JSON字符串
23 contentType : "application/json;charset=UTF-8",
24 //定义回调响应的数据格式为JSON字符串,该属性可以省略
25 dataType : "json",
26 //成功响应的结果
27 success : function(data){
28 if(data != null){
29 alert("您输入的用户名为:"+data.username+
30 "密码为:"+data.password);
31 }
32 }
33 });
34 }
35 </script>
36 </head>
37 <body>
38 <form>
39 用户名:<input type="text" name="username" id="username"><br />
40 密 码:
41 <input type="password" name="password" id="password"><br />
42 <input type="button" value="测试JSON交互" onclick=" testJson()" />
43 </form>
44 </body>
45 </html>
在文件4中,编写了一个测试JSON交互的表单,当单击按钮“测试JSON交互”时,会执行页面中的testJson()函数。在函数中使用了jQuery的AJAX方式将JSON格式的用户名和密码传递到以“/testJson”结尾的请求中。
需要注意的是,在AJAX中包含了3个特别重要的属性,其说明如下:
● data:即请求时携带的数据,当使用JSON格式时,要注意编写规范;
● contentType:当请求数据为JSON格式时,值必须为application/json;
● dataType:当响应数据为JSON时,可以定义dataType属性,并且值必须为json。其中dataType : "json"也可以省略不写,页面会自动识别响应的数据格式。
小提示:
在上述测试页面index.jsp中使用的是jQuery的AJAX进行的JSON数据提交和响应,所以还需要引入jquery.js文件。本示例是引入了WebContent目录下js文件夹中的jquery-1.11.3.min.js,读者可在所提供的源码中找到此文件。
(6)在src目录下,创建一个com.itheima.controller包,在该包下创建一个用于用户操作的控制器类UserController,编辑后的代码如文件5所示。
文件5 UserController.java
1 package com.itheima.controller;
2 import org.springframework.stereotype.Controller;
3 import org.springframework.web.bind.annotation.RequestBody;
4 import org.springframework.web.bind.annotation.RequestMapping;
5 import org.springframework.web.bind.annotation.ResponseBody;
6 import com.itheima.po.User;
7 @Controller
8 public class UserController {
9 /**
10 * 接收页面请求的JSON数据,并返回JSON格式结果
11 */
12 @RequestMapping("/testJson")
13 @ResponseBody
14 public User testJson(@RequestBody User user) {
15 // 打印接收的JSON格式数据
16 System.out.println(user);
17 // 返回JSON格式的响应
18 return user;
19 }
20 }
在文件5中,使用注解方式定义了一个控制器类,并编写了接收和响应JSON格式数据的testJson()方法,在方法中接收并打印了接收到的JSON格式的用户数据,然后返回了JSON格式的用户对象。
方法中的@RequestBody注解用于将前端请求体中的JSON格式数据绑定到形参user上,@ResponseBody注解用于直接返回User对象(当返回POJO对象时,会默认转换为JSON格式数据进行响应)。
(7)将chapter14项目发布到Tomcat服务器并启动,在浏览器中访问地址http://localhost:8080/chapter14/index.jsp
,其显示效果如图2所示。
图2 index.jsp测试页面
在两个输入框中分别输入用户名“jack”和密码“123456”后,单击“测试JSON交互”按钮,当程序正确执行时,页面中会弹出显示用户名和密码的弹出框,如图3所示。
图3 index.jsp测试页面
与此同时,Eclipse的控制台中也会显示相应数据,如图4所示。
图4 运行结果
从图3和图4的显示结果可以看出,编写的代码已经正确实现了JSON数据交互,可以将JSON格式的请求数据转换为方法中的Java对象,也可以将Java对象转换为JSON格式的响应数据。
多学一招:JSON数据的其他转换配置
1. 使用<bean>标签方式的JSON转换器配置
在配置JSON转换器时,除了常用的<mvc:annotation-drivern />方式配置外,还可以使用<bean>标签的方式进行显示的配置。具体配置方式如下:
<!-- <bean>标签配置注解方式的处理器映射器和处理器适配器必须配对使用-->
<!-- 使用<bean>标签配置注解方式的处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method
.annotation.RequestMappingHandlerMapping" />
<!-- 使用<bean>标签配置注解方式的处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method
.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<!-- 在注解适配器中配置JSON转换器 -->
<bean class="org.springframework.http.converter.json
.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
从上述示例可以看出,使用<bean>标签配置方式配置JSON转换器时,需要同时配置处理器映射器和处理器适配器,并且JSON转换器是配置在适配器中。
2. 配置静态资源访问的方式
除了使用<mvc:resources>元素可以实现对静态资源的访问外,还有另外2种静态资源访问的配置方式,具体分别如下:
(1)使用<mvc:default-servlet-handler>标签。
在springmvc-config.xml文件中,使用<mvc:default-servlet-handler>标签,具体如下:
<mvc:default-servlet-handler />
在springmvc-config.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler(即默认的Servlet请求处理器)。它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web服务器默认的Servlet处理,默认的Servlet就会对这些静态资源放行,如果不是静态资源的请求,才由DispatcherServlet继续处理。
注意:
一般Web服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你使用的Web应用服务器默认的Servlet名称不是"default",则需要通过default-servlet-name属性显示指定,具体方式如下:
<mvc:default-servlet-handler default-servlet-name="Servlet名称" />
而Web服务器的Servlet名称是由使用的服务器确定的,常用服务器及其Servlet名称如下:
● Tomcat, Jetty, JBoss, and GlassFish默认Servlet的名称—— "default";
● Google App Engine默认Servlet的名称——"_ah_default";
● Resin默认Servlet的名称——"resin-file";
● WebLogic默认Servlet的名称—— "FileServlet";
● WebSphere默认Servlet的名称——"SimpleFileServlet"。
(2)激活Tomcat默认的Servlet来处理静态文件访问。
激活Tomcat默认的Servlet时,需要在web.xml中添加以下内容:
<!--激活tomcat的静态资源拦截,需要哪些静态文件,再往下追加-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
...
在上述代码中,配置了<servlet-mapping>元素来激活Tomcat默认的Servlet来处理静态文件,我们还可以根据需要继续追加<servlet-mapping>。此种配置方式和上一种方式本质上是一样的,都是使用Web服务器默认的Servlet来处理静态资源文件的访问。其中Servelt名称(即<servlet-name>元素的值)也是由使用的服务器来确定的,不同的服务器需要使用不同的名称。
以上3种静态资源访问的配置方式不同,并且各有优缺点,具体如下:
● 第一和第三种配置方式可以选择性的释放静态资源;
● 第二种配置方式配置相对简单,只需要一行代码,就可以释放所有静态资源;
● 第二和第三种配置方式会导致项目移植性较差,需要根据具体的Web服务器来更改Servlet名称;
● 第三种配置方式运行效率更高,因为服务器启动时已经加载了web.xml中的静态资源。
在实际开发中,更为常用的配置还是第一种(即案例中的)配置方式,这样就不需要考虑Web服务器的问题了。