- JSON
-
在实际开发中,通常需要和别的系统交换数据,数据交换的格式通常有XML和JSON等;
-
XML:是一门扩展标记语言,他虽然也能让我们与其他系统交换数据,但是他的缺点也很明显
- XML文件格式文件庞大, 格式复杂, 传输占用带宽
- 服务器端和客户端都需要花费大量代码来解析XML, 不论服务器端和客户端代码变的异常复杂和不容易维护
- 客户端不同浏览器之间解析XML的方式不一致, 需要重复编写很多代码
- 服务器端和客户端解析XML花费资源和时间
-
XML表示一个对象和多个对象
<!-- XML表示一个对象 -->
<user>
<username>张三</username>
<age>16</age>
<sex>true</sex>
<dept>
<id>001</id>
<name>开发部</name>
</dept>
</user>
<!-- XML表示多个对象 -->
<users>
<username>张三</username>
<age>16</age>
<sex>true</sex>
<dept>
<id>001</id>
<name>开发部</name>
</dept>
<username>李四</username>
<age>26</age>
<sex>false</sex>
<dept>
<id>003</id>
<name>测试部</name>
</dept>
<username>王五</username>
<age>36</age>
<sex>true</sex>
<dept>
<id>002</id>
<name>运维部</name>
</dept>
</users>
复制代码
-
JSON:是一种基于JavaScript 语法开放的轻量级数据交换格式,使用js语法来描述数据对象
- JSON作为一个轻量级的数据格式,相对于XML,文档更小,结构清晰简洁,读写效率更高
-
JSON的数据格式:
- 一个对象:{"key1":"value1","key2":"value2"...}
- 多个对象:[{"key1":"value1","key2":"value2"...},{"key1":"value1","key2":"value2"...},{"key1":"value1","key2":"value2"...}]
注意:JSON数据格式就是以 "key":value 形式的数据格式,KEY必须 以 "" 包裹起来,并且如果value是字符串也必须以 "" 包裹起来,如果不是字符串那么正常写就可以
- JSON表示一个对象和多个对象
JSON表示一个对象:
{"username":"张三","age":16,"sex":true,"dept":{"id":001,"name":"开发部"}}
格式化后的样子:
{
"username":"张三",
"age":16,
"sex":true,
"dept":{
"id":001,
"name":"开发部"
}
}
JSON表示多个对象:
[
{"username":"张三","age":16,"sex":true,"dept":{"id":001,"name":"开发部"}},
{"username":"李四","age":26,"sex":false,"dept":{"id":003,"name":"测试部"}},
{"username":"王五","age":36,"sex":true,"dept":{"id":002,"name":"运维部"}}
]
复制代码
- SpringMVC返回JSON
-
有时候后台需要向前台传递JSON格式的数据,那么这个时候要把Java对象转换为JSON,就需要第三方的支持:
- Jackson:jackson.codehaus.org/
- JSON-lib:json-lib.sourceforge.net/
- Gson:code.google.com/p/google-gs…
- FastJson阿里开源
- SpringMVC返回JSON方式一:手动拼接
/**
* springMVC响应JSON数据格式方式一:
手动拼接的方式通过HttpServletResponse返回,这种原始方式不需要引入第三方JSONjar包
* @param resop
* @throws Exception
*/
@RequestMapping("/01")
public void getJson(HttpServletResponse resop) throws Exception{
// 告诉浏览器,以什么方式解析我的数据,并且指定编码字符集
resop.setContentType("application/json;charset=utf-8");
// 获取打印输出流
PrintWriter writer = resop.getWriter();
// 设置打印的字符串,手动拼接JSON格式字符串
writer.write("{"name":"张三","age":18}");
}
复制代码
- SpringMVC返回JSON方式二:使用注解返回对象
-
通过@ResponseBody注解,将java对象转为JSON格式的数据
- 加入jackson工具包
/**
* springMVC响应JSON数据格式方式二:通过@ResponseBody注解的方式响应对象
* @return
*/
@RequestMapping("/02")
// 将返回的数据,响应成JSON格式给浏览器,但是要注意需要导入JSONjar包
@ResponseBody
public User getUser() {
return new User("王二麻子",17L,true,new Date());
}
/**
* springMVC响应JSON数据格式方式三:通过@ResponseBody注解的方式返回多个对象
* @return
*/
@RequestMapping("/03")
// 将返回的数据,响应成JSON格式给浏览器,但是要注意需要导入JSONjar包
@ResponseBody
public List<User> getUser2() {
return Arrays.asList(new User("王二麻子",17L,true,new Date()),
new User("王三码子",17L,true,new Date()),
new User("王四马子",17L,true,new Date()));
}
/**
* springMVC响应JSON数据格式方式四:通过@ResponseBody注解的方式返回Map
* @return
*/
@RequestMapping("/04")
// 将返回的数据,响应成JSON格式给浏览器,但是要注意需要导入JSONjar包
@ResponseBody
public HashMap<String, String> getUser3() {
HashMap<String,String> hashMap = new HashMap<String,String>();
hashMap.put("name", "李四");
hashMap.put("age", "18");
return hashMap;
}
复制代码
- Json中对日期格式的特殊处理
-
在返回对象中,如果有时间字段,那么浏览器默认展示的是时间戳,所以我们需要进行格式化
-
从后台向前台:
- 在日期get属性字段上,添加一个格式化注解
- import com.fasterxml.jackson.annotation.JsonFormat;
- @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-
从前台到后台
- 在日期set属性字段上或者是属性上,添加一个格式化注解
- @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
-
注意:
- 出现406状态异常:缺少jar包,加入jackson的jar包即可;
- 使用@ResponseBody注解之后,不会经过视图解析器,意思就是如果不想返回页面或者说想返回JSON格式数据就必须加上此注解;
- 如果在ie中测试,会弹出下载文件的窗口,可以在spring-mvc.xml的mvc:annotation-driven中加入以下配置解决:
<!-- 开启spring对MVC的支持 -->
<mvc:annotation-driven>
<!-- 避免在IE浏览器中返回JSON时出现下载文件的情况 -->
<mvc:message-converters>
<bean id= "mappingJackson2HttpMessageConverter"
class= "org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name= "supportedMediaTypes" >
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
复制代码
- SpringMVC文件上传
- 文件上传:指的是将本地的文件复制到服务器上;
- SpringMvc中的文件上传是对原生文件上传的封装,目的是,较少代码量,提高开发效率;
- 文件上传三要素:
- 表单的提交的方式必须是POST请求(get请求对提交的数据)
- 表单中必须有一个文件上传项:<input type="file" name="headImg"/>,文件上传项必须有name属性和值;
- 表单的enctype属性的值必须是multipart/form-data
- 文件上传步骤
- 第一步:编写文件上传页面
<form action= "/upload2" method= "post" enctype= "multipart/form-data" >
username:<input name= "username" type= "text" ><br/>
头像: <input name= "headImg" type= "file" ><br/>
<!-- button按钮在form表单内部,默认就是submit提交 -->
<button>提交</button>
</form>
复制代码
- 第二步:添加文件上传jar包
- 由于SpringMVC自己没有实现文件上传,它使用的是apache.commons.fileupload
- com.springsource.org.apache.commons.fileupload-1.2.0.jar
- com.springsource.org.apache.commons.io-1.4.0.jar
复制代码
- 第三步:配置文件上传解析器
- SpringMVC使用MultipartFile来进行文件上传,所以我们首先要配置MultipartResolver,用于处理表单中的file,如果没有配置就会报如下错误:提示告诉开发者你没有配置文件上传解析器:
复制代码
- 配置MultipartResolver:注意id="multipartResolver"的id值不能乱写
<!-- 配置文件上传解析器 -->
<!-- 注意:id不能乱写,必须叫这个,否则上传解析器不生效 -->
<bean id= "multipartResolver" class= "org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<!-- 设置最大上传10MB -->
<property name= "maxUploadSize" value= "10485760" />
<!-- 配置编码字符集 -->
<property name= "defaultEncoding" value= "UTF-8" />
</bean>
复制代码
- 第四步:后台控制器处理上传
package cn.itsource._02_upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class UploadController {
// 简单版本的文件上传
@RequestMapping("/upload")
@ResponseBody
public void upload(String username, MultipartFile headImg) throws Exception{
// System.out.println(username);
// System.out.println(headImg);
// // 获取的就是文件的类型
// String contentType = headImg.getContentType();
// // 获取的就是文件的名称
// String originalFilename = headImg.getOriginalFilename();
// // 获取的就是在form表单里面的文件名称(不常用)
// String name = headImg.getName();
// // 获取的就是文件的大小
// long size = headImg.getSize();
// System.out.println("contentType: " + contentType);
// System.out.println("originalFilename: " + originalFilename);
// System.out.println("name: " + name);
// System.out.println("size: " + size);
// 拿到文件输入流
InputStream inputStream = headImg.getInputStream();
// 创建文件输出流
FileOutputStream outputStream = new FileOutputStream("/Users/colin/Desktop/aaa.png");
// 将文件复制到指定的地方
IOUtils.copy(inputStream, outputStream);
outputStream.close();
inputStream.close();
}
/**
* 完整版本的文件上传
* 需求:
* 1.上传的附件保存在webapp下面
* 2.文件的名称随机
* 3.文件的后缀根据上传的附件后缀决定
* @param username
* @param headImg
* @throws Exception
*/
@RequestMapping("/upload2")
@ResponseBody
public void upload2(String username, MultipartFile headImg,HttpServletRequest request) throws Exception{
// 拿到附件名称
String originalFilename = headImg.getOriginalFilename();
// 通过工具类,拿到附件类型后缀
String extension = FilenameUtils.getExtension(originalFilename);
// 拿到上下文对象
ServletContext servletContext = request.getServletContext();
// 拿到上下文路径 /Users/colin/eclipse-workspace/springMVC_JSON/webapp
String contextPath = servletContext.getRealPath("/");
String fileName = System.currentTimeMillis() + "." + extension;
// 根据路径创建文件流
File file = new File(contextPath + "upload/" + fileName);
// 判断是否有父文件路径,如果没有创建出来
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// 根据指定路径创建输出流
OutputStream OutputStream = new FileOutputStream(file);
// 将附件转为输入流
InputStream inputStream = headImg.getInputStream();
// 复制文件到本地
IOUtils.copy(inputStream,OutputStream);
inputStream.close();
OutputStream.close();
}
}
- SpringMVC文件下载
- 文件下载:就是将服务器(表现在浏览器中)中的资源下载(复制)到本地磁盘;
- 第一种方式:
- 在webapp下面创建文件夹download,并在文件夹下方放入三个不同类型的文件
- 创建一个JSP页面,写入三个超链接
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/download/1.zip">这是一个附件</a><br/>
<a href="/download/2.png">这是一张图片</a><br/>
<a href="/download/3.txt">这是一个文本文档</a>
</body>
</html>
复制代码
- 通过测试,我们可以发现,如果只使用超链接的方式,浏览器能够解析的图片、文本文档,是可以直接打开的,而压缩文件浏览器是给我们下载的。这是因为浏览器会自动识别是否能够打开文件,不能打开才进行下载,这很明显不符合我们的文件下载要求,这时候我们就要通过后台代码来处理
- 第二种方式:
- 改变jsp页面,因为需要我们后台处理下载,所以需要将要下载的文件名称传递到后台
<%@page import="java.net.URLEncoder"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/download?fileName=1.zip">这是一个附件</a><br/>
<a href="/download?fileName=2.png">这是一张图片</a><br/>
<a href="/download?fileName=3.txt">这是一个文本文档</a><br/>
</body>
</html>
复制代码
- 创建DownloadController类
package cn.itsource.download;
import java.io.File;
import java.io.FileInputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class DownloadController {
@RequestMapping("/download")
@ResponseBody
public void download(HttpServletResponse response,HttpServletRequest request,String fileName) throws Exception {
// 设置请求头,告诉浏览器文件下载的名字,附件表示做下载或上传操作,浏览器就不会将文件的内容直接显示出来了
// attachment:告诉浏览器,以弹框的方式打开我的附件
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
// 通过上下文对象获取附件
ServletContext servletContext = request.getServletContext();
// 通过上下文对象获取download绝对路径
String realPath = servletContext.getRealPath("/download");
// 根据父路径和要下载的文件名称得到文件流
File file = new File(realPath,fileName);
// 将文件流给FileInputStream 创建输入流
FileInputStream fileInputStream = new FileInputStream(file);
// 通过response 拿到输出流
ServletOutputStream outputStream = response.getOutputStream();
// 下载的核心代码
IOUtils.copy(fileInputStream, outputStream);
// 关闭流
outputStream.close();
fileInputStream.close();
}
}
复制代码
注意:想要浏览器以下载的形式打开我们的文件,那么设置请求头是关键
- 解决中文问题
- 做完上面的步骤,貌似我们的下载功能已经完成了,但是各位注意,如果你的文件名称是中文,那么就会发生下面的情况
- 改变文件名称以及文本文档下载链接的名称,3.txt为中文.txt
- 测试点击中文下载
- 我们的文件名称因为是中文,浏览器无法解析,所以变成了上面的样子,这是主流浏览器,这时候我们再来看一下IE浏览器
- 可以发现,当文件名称是中文时,普通浏览器文件名称会为空或者乱码。IE浏览器会400,因为IE浏览器认为你的文件名称就是不能是中文的,这时候我们就要做一个兼容IE的操作,分别将主流浏览器和IE浏览器区别开来处理
- JSP页面处理
<%@page import="java.net.URLEncoder"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/download?fileName=1.zip">这是一个附件</a><br/>
<a href="/download?fileName=2.png">这是一张图片</a><br/>
<!-- 给中文编码,这样传递的时候就不是中文了,在后台解码即可 -->
<a href="/download?fileName=<%=URLEncoder.encode("中文.txt","UTF-8")%>">这是一个文本文档</a><br/>
</body>
</html>
复制代码
- 后台处理
package cn.itsource.download;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class DownloadController {
@RequestMapping("/download")
@ResponseBody
public void download(HttpServletResponse response,HttpServletRequest request,String fileName) throws Exception {
//获取请求头信息,这个属性值可以知道是那种浏览器,每个浏览器有独特的标识
String header = request.getHeader("User-Agent");
//编码后的名字
String fileNameEncoder = "";
//证明是IE浏览器
if(header.toUpperCase().contains("MSIE") || header.toUpperCase().contains("TRIDENT")){
//对中文进行编码
fileNameEncoder = URLEncoder.encode(fileName,"UTF-8");
}else{
//主流浏览器
fileNameEncoder = new String(fileName.getBytes("UTF-8"),"ISO-8859-1");
}
// 设置文件下载的名字 -- 附件表示做下载或上传操作,浏览器就不会将文件的内容直接显示出来了
// attachment:告诉浏览器,以弹框的方式打开我的附件
response.setHeader("Content-Disposition", "attachment; filename=" + fileNameEncoder);
// 通过上下文对象获取附件
ServletContext servletContext = request.getServletContext();
// 通过上下文对象获取download绝对路径
String realPath = servletContext.getRealPath("/download");
// 根据父路径和要下载的文件名称得到文件流
File file = new File(realPath,fileName);
// 将文件流给FileInputStream 创建输入流
FileInputStream fileInputStream = new FileInputStream(file);
// 通过response 拿到输出流
ServletOutputStream outputStream = response.getOutputStream();
// 下载的核心代码
IOUtils.copy(fileInputStream, outputStream);
// 关闭流
outputStream.close();
fileInputStream.close();
}
}
复制代码
注:Microsoft Edge和IE的最大区别就是Edge 是windows 10 之后有微软推出的浏览器,而在windows 10 之前微软系统自家浏览器都是IE;
- SpringMVC拦截器
- 在springMVC第一天的时候,我们学习了过滤器,SpringMVC也给我们提供了一个相似功能的拦截器
- 拦截器与过滤器的区别、相同点
- 功能都是一样的
- 过滤器是tomcat的
- 拦截器是SpringMVC的
- 使用步骤
-
创建一个拦截器类,实现HandlerInterceptor接口并实现三个方法
- preHandle:拦截器核心方法,会在所有请求进入目标方法之前执行,返回参数true/false,true放行,false拦截
- postHandle:在执行完目标方法之后,返回页面之前执行
- afterCompletion:在页面渲染完毕之后,展示页面之前执行
package cn.itsource.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
// HandlerInterceptor 拦截器的接口
public class MyInterceptor implements HandlerInterceptor{
/**
* 拦截器核心方法,会在所有请求进入目标方法之前执行
* 返回值类型:true 放行 false 不放行
*/
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("请求进入拦截器了。。。。。。");
return true;
}
/**
* 在执行完目标方法之后,返回页面之前执行
*/
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("进入方法了,并且在返回页面之前执行。。。。。。");
}
/**
* 在页面渲染完毕之后,展示页面之前执行
*/
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("页面已经渲染完毕了,并且在返回页面之前执行。。。。。。");
}
}
复制代码
- 在spring-mvc.xml中配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- /*:拦截所有请求,但是在springMVC的拦截器中如果使用此方式那么他只会拦截一级请求
一级请求:可以拦截的:/a /b /c 不能拦截的:/a/b /b/c /c/v/n
/**:拦截所有请求,但是他可以拦截多级路径 例如:/a /b /c /a/b /b/c /c/v/n
-->
<mvc:mapping path="/**"/>
<bean class="cn.itsource.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
复制代码
- SpringMVC执行流程
- SpringMVC是一个面试高频题,所以我们来大概了解一下他的执行流程,流程图如下:
-
流程描述:控制器即处理器(Handler)
-
1. 用户向服务器发送请求,请求会统一交给SpringMVC前端控制DispatcherServlet处理;
-
2. DispatcherServlet通过请求HandlerMapping(处理器映射管理对象)找到该请求对应的Handler对象(包括控制器以及Handler对象对应的拦截器) 和HandlerExecutionChain对象(包含:控制器+2个拦截器);
-
3. DispatcherServlet请求HandlerAdapter,选择一个合适的HandlerAdapter去处理Handler。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法);
-
4. 提取request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
- 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
-
5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
-
6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet;
-
7. ViewResolver 结合Model和View,来渲染视图(Model+View合成)
-
8. 将渲染结果返回给客户端;
-
-
简易描述
-
1.客户端将请求统一提交到DispatcherServlet;
-
2.DispatcherServlet会将请求交给HandlerMapping进行请求映射,匹配该请求的Handler;
-
3.DispatcherServlet再请求HandlerAdapter调用相应的Handler处理请求,并向前端控制器返回一个ModelAndView对象;
-
4.DispatcherServlet将ModelAndView对象交给ViewResoler视图解析器处理,返回指定的视图View;
-
5.DispatcherServlet 对 View 进行渲染(即将模型数据填充至视图中);
-
6.DispatcherServlet 将页面响应给用户;
SpringMVC高级 + JSON 我们就学习完了
-