javaweb实训第六天上午——JSON&SpringMVC进阶

Posted dearQiHao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javaweb实训第六天上午——JSON&SpringMVC进阶相关的知识,希望对你有一定的参考价值。

1.课程介绍

  1. JSON; (了解)
  2. SpringMVC返回JSON; (掌握)
  3. SpringMVC文件上传、下载; (掌握)
  4. SpringMVC拦截器; (掌握)
  5. SpringMVC执行流程; (掌握)

2.JSON

2.1.JSON概述

  1. 在实际开发中,通常需要和别的系统交换数据,数据交换的格式通常有XML和JSON等;
  2. JSON(javascript Object Notation:JavaScript对象表示法)是一种基于JavaScript 语法开放的轻量级数据交换格式,使用js语法来描述数据对象;
  3. JSON作为一个轻量级的数据格式,相对于XML,文档更小,结构清晰简洁,读写效率更高,XML需要很多的标签,在数据传输的时候无疑会消耗更多网络资源和流量:
    (1)用XML表示一个对象:
<person id="1">
<name>tom</name>
<age>20</age>
<salary>5000.0</salary>
</person>

(2)用JSON表示一个对象:{“id”:1,“name”:“tom”,“salary”:5000.0}

2.2.JSON语法

2.2.1.如何用JSON表示一个对象

简单对象:

var obj={“name”:“tom”,“age”:34}; alert(obj.name); //tom

复杂对象:

var obj={“name”:“vikey”,“address”:{“city”:“成都”,“street”:“九眼桥”,“room”:“215”}}; alert(obj.address.street); //九眼桥

2.2.2.如何用JSON表示一个数组

语法:

var obj = [value,value,value]
value可以是ob

ject、数组、简单数据类型(string、number、boolean等)、json对象
例子:

var obj = [
{”id”:1,”name”:”tom1”,”salary”:5000.0},
{”id”:2,”name”:”tom2”,”salary”:6000.0},
{”id”:3,”name”:”tom3”,”salary”:7000.0}
]
alert(obj.length);
alert(obj[1].name);

遍历:
注意:json中数据的属性名必须用双引号或单引号引起来(双引号是标准格式,单引号是非标准格式),属性值如果是字符型必须用引号引起来;

2.2.3.JSON字符串和JSON对象

JSON字符串:
var obj ={“id”:1,”name”:”tom”,”salary”:5000.0};
alert(obj.id);	X
Json对象:
var obj = {“id”:1,”name”:”tom”,”salary”:5000.0};
alert(obj.id);		//1
alert(obj.name);	   //tom

3.SpringMVC返回JSON

1.有时候后台需要向前台传递JSON格式的数据,那么这个时候要把Java对象转换为JSON,就需要第三方的支持:
  (1)Jackson:http://jackson.codehaus.org/
  (2)JSON-lib:http://json-lib.sourceforge.net/
  (3)Gson:http://code.google.com/p/google-gson/
  (4)FastJson阿里开源

3.1.Java对象转成JSON格式的数据

1.加入jackson工具包:
在这里插入图片描述

2.后台编程(使用@ResponseBody注解):
在这里插入图片描述

3.2.Json中对日期格式的特殊处理

从后台向前台:
(1)默认返回的日期格式为时间戳,而在前台我们希望显示出指定规则的日期字符串:
  默认:{“name”:“小明哥”,“birthdate”:121223223}
  期望: {“name”:“小明哥”,“birthdate”:“2025-12-12 14:12:12”}
(2)在日期get属性,字段上,添加一个格式化注解
  import com.fasterxml.jackson.annotation.JsonFormat;
  @JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”,timezone=“GMT+8”)
从前台向后台:
(1)在后台模型的setter方法上,添加注解:
  @DateTimeFormat(pattern=“yyyy-MM-dd HH:mm:ss”)
(2)访问地址:localhost:8080/jsonV587?birthDay=2017-06-13 16:50:53
(3)后台接收:

@RequestMapping("/getJson")
@ResponseBody//将返回的数据自动转换成json格式的数据,而且是用了@ResponseBody,不会经过视图解析器
public Date getJson(User user){//接收的用对象接收,直接用Date类型的参数会报400(请求无效)
	System.out.println(user.getBirthday());
	return user.getBirthday();
}

3.3.注意事项

  1. 出现406状态异常:缺少jar包,加入jackson的jar包即可;
  2. 使用@ResponseBody注解之后,不会经过视图解析器;
  3. 如果在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>

4.文件上传与下载

4.1.文件上传

  1. 文件上传:指的是将本地的文件复制到服务器上;
  2. SpringMvc中的文件上传是对原生文件上传的封装,目的是,较少代码量,提高开发效率;
  3. 文件上传三要素:
      (1)表单的提交的方式必须是POST请求(get请求对提交的数据)
      (2)表单中必须有一个文件上传项:,文件上传项必须有name属性和值;
      (3)表单的enctype属性的值必须是multipart/form-data

4.1.1.添加jar文件

  1. 由于SpringMVC自己没有实现文件上传,它使用的是apache.commons.fileupload
      com.springsource.org.apache.commons.fileupload-1.2.0.jar
      com.springsource.org.apache.commons.io-1.4.0.jar

4.1.2.jsp页面

在这里插入图片描述

4.1.3.配置上传解析器
SpringMVC使用MultipartFile来进行文件上传,所以我们首先要配置MultipartResolver,用于处理表单中的file,如果没有配置就会报如下错误:提示告诉开发者你没有配置文件上传解析器:

配置MultipartResolver:注意id="multipartResolver"的id值不能乱写

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!-- 设置上传文件的最大尺寸为1MB -->
	<property name="maxUploadSize">
		<!-- spring el写法:1MB -->
		<value>#{1024*1024}</value>
	</property>
     <!-- 效果同上 -->
<property name="maxUploadSize" value="1048576" />
</bean>

4.1.4.后台处理

@Controller
public class UploadController {
	@RequestMapping(value="/upload",method=RequestMethod.POST)
	public String uploadFile(MultipartFile fileUpload,String name,HttpServletRequest req) throws FileNotFoundException, IOException {
		
		System.out.println("普通表单获取方式:" + name);
		
		//上传表单信息:注意MultipartFile对象的名称必须与上传表单项的name属性值一致
		System.out.println("上传文件是否为空:" + fileUpload.isEmpty());
		System.out.println("上传文件的大小(字节):" + fileUpload.getSize());
		System.out.println("上传文件的类型:" + fileUpload.getContentType());
		System.out.println("上传表单name属性值:" + fileUpload.getName());
		System.out.println("上传文件名:" + fileUpload.getOriginalFilename());

	// 获取upload真实路径:一大巨坑(uploadFile千万不要和请求upload一样的名字,否则第二次上传出现405)
		String realPath = req.getServletContext().getRealPath("/uploadFile");
		File file = new File(realPath);
		if (!file.exists()) {// 如果upload文件夹不存在,就创建
			file.mkdirs();
		}

		String prefix = UUID.randomUUID().toString().replaceAll("-", "");
//使用UUID加前缀命名文件,防止名字重复被覆盖
		String fileName = prefix+"_"+fileUpload.getOriginalFilename();

		InputStream in= fileUpload.getInputStream();;//声明输入输出流
		OutputStream out=new FileOutputStream(new File(realPath+"\\\\"+fileName));//指定输出流的位置;

		//使用IOUtils.copy实现文件复制 
		IOUtils.copy(in, out);
		System.out.println("上传成功");
		in.close();
		out.close();
		return "redirect:/upload.jsp";
	}
}

4.2.文件下载

文件下载:就是将服务器(表现在浏览器中)中的资源下载(复制)到本地磁盘;

4.2.1.前台代码

  1. 前台使用超链接,超链接转到后台控制器,在控制器通过流的方式进行操作;
    在这里插入图片描述

4.2.2.后台代码

@Controller
public class DownloadController {
	@RequestMapping("/download")
	public void download(String filename,HttpServletRequest req,HttpServletResponse resp) throws Exception{
		//1.获取输入流
		//1.1.获取文件在服务器的绝对路径
		String parentPath = req.getServletContext().getRealPath("/download");
		File file = new File(parentPath, filename);
		if(file.exists()){
			FileInputStream in = new FileInputStream(file);

			//2.获取输出流
			//2.1.设置文件下载的名字  -- 附件表示做下载或上传操作,浏览器就不会将文件的内容直接显示出来了
			resp.setHeader("Content-Disposition", "attachment; filename=" + filename);
			//2.2.获取输出流
			ServletOutputStream out = resp.getOutputStream();

			//3.实现下载
			IOUtils.copy(in, out);
			//关流,释放资源
			out.close();
			in.close();
		}
	}
}

4.2.3.解决中文问题

问题描述:当下载 “美女.rar”,问题就出现了?
在这里插入图片描述

解决方法:兼容IE、edge和其他浏览器
分析: 如果是IE或edge就用URLEncoder,如果是其他浏览器就要用以下方式解决:

new String(name.getBytes("UTF-8"),"ISO-8859-1")
user–agent:这个请求头是指用户代理的意思,告诉服务器使什么浏览器;
@Controller
public class DownloadController {
	@RequestMapping("/download")
	public void download(String filename,HttpServletRequest req,HttpServletResponse resp) throws Exception{

		//1.获取输入流
		//1.1.获取文件在服务器的绝对路径
		String parentPath = req.getServletContext().getRealPath("/download");
		File file = new File(parentPath, filename);
		if(file.exists()){
			FileInputStream in = new FileInputStream(file);

			//2.获取输出流
			//2.1.中文文件名称处理(ie和edge都是微软的浏览器 -- 处理方式一样)
//区分浏览器,User-Agent中有浏览器的信息,trident是ie引擎名称,具体:
//mozilla/5.0 (windows nt 6.2; win64; x64; trident/7.0; rv:11.0) like gecko【eclipse自带ie浏览器】
//Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 
		    //Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763【电脑安装的edge浏览器】
//mozilla/5.0 (windows nt 10.0; win64; x64; rv:68.0) gecko/20100101 firefox/68.0【火狐】
//mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/73.0.3683.86 safari/537.36【谷歌】
			//ie浏览器
			if(req.getHeader("User-Agent").toUpperCase().indexOf("TRIDENT")!=-1){
				filename = URLEncoder.encode(filename, "utf-8");
				//电脑自带edge【edʒ】浏览器	
			}else if(req.getHeader("User-Agent").toUpperCase().indexOf("EDGE")!=-1){		
				filename = URLEncoder.encode(filename, "utf-8");
			}else{//其他浏览器
				filename = new String(filename.getBytes("UTF-8"),"ISO-8859-1");//转码的方式
			};

			//2.2.设置文件下载的名字  -- 附件表示做下载或上传操作,浏览器就不会将文件的内容直接显示出来了
			resp.setHeader("Content-Disposition", "attachment; filename=" + filename);
			//2.3.获取输出流
			ServletOutputStream out = resp.getOutputStream();
			//3.实现下载
			IOUtils.copy(in, out);
			//关流,释放资源
			out.close();
			in.close();
		}
	}
}		

页面:

<a href='/download?fileName=<%=URLEncoder.encode("美女.jpg", "utf-8") %>'>美女.jpg</a>

注:Microsoft Edge和IE的最大区别就是Edge 是windows 10 之后有微软推出的浏览器,而在windows 10 之前微软系统自家浏览器都是IE;

5.SpringMVC拦截器

5.1.创建拦截器

public class MyInterceptor implements HandlerInterceptor {

	//preHandle()方法在业务处理器处理请求之前被调用   
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("进入拦截器了....");
		//这里可以判断用户是否登录 
		//没有登录可以使用 request/response跳转回登录页面
		
		//注:如果不继续执行返回false,否则返回true
		return false;
	}

	// postHandle()方法在业务处理器处理请求之后被调用   
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	}
	 
	// afterCompletion()方法在DispatcherServlet完全处理完请求后被调用   
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
}

5.2.配置拦截器

<!-- 配置拦截器组 -->
<mvc:interceptors>
	<!-- 拦截器 -->
	<mvc:interceptor>
		<!-- 要拦截的配置,该配置必须写在不拦截的上面,/*拦截一级请求,/**拦截多级请求 -->
		<mvc:mapping path="/**"  />
		<!-- 设置不拦截的配置 -->
		<mvc:exclude-mapping path="/login"/>
		<!-- 配置拦截器 -->
		<bean class="cn.itsource.springmvc._06_interceptor.MyInterceptor" />  
	</mvc:interceptor>
</mvc:interceptors>

6.SpringMVC执行流程

6.1.流程图

在这里插入图片描述

6.2.流程描述

注:控制器即处理器(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. 将渲染结果返回给客户端;

6.3.SpringMVC工作流程描述(简易版本:面试)

  1. 客户端将请求统一提交到DispatcherServlet;
  2. DispatcherServlet会将请求交给HandlerMapping进行请求映射,匹配该请求的Handler;
  3. DispatcherServlet再请求HandlerAdapter调用相应的Handler处理请求,并向前端控制器返回一个ModelAndView对象;
  4. DispatcherServlet将ModelAndView对象交给ViewResoler视图解析器处理,返回指定的视图View;
  5. DispatcherServlet 对 View 进行渲染(即将模型数据填充至视图中);
  6. DispatcherServlet 将页面响应给用户;

7.课程总结

7.1.重点

  1. SpringMvc返回Json数据;
  2. SprignMvc文件上传;
  3. SpringMvc执行流程;

7.2.难点

  1. SprignMvc文件上传;
  2. SpringMvc执行流程;

7.3.如何掌握

  1. 课上认真听课;
  2. 完成课后练习;
  3. 抓住课程重点;

7.4.排错技巧

  1. 通过异常和错误找出问题,分析问题,解决问题;
  2. 还原代码;
  3. 代码最小化;
  4. 断点;

8.常见问题

  1. 状态异常:如果出现406状态异常,jackson的jar文件没有导入
  2. HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
    分析:缺少配置

9.课后练习

  1. 课堂代码1-2遍;

10.面试题

  1. SpringMVC执行流程?

11.扩展知识或课外阅读推荐(可选)

11.1.扩展知识

拦截器(interceptor)与过滤器(filter)的区别?

  1. 拦截器与过滤器有很多相似之处,都可以拦截请求,也可以放行;
  2. 拦截器是SpringMVC中的组件,需要在Spring的配置文件中配置。而过滤器是JavaEE中的组件,在web.xml中配置。Filter的执行顺序在Interceptor之前;
  3. 拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor;
    在这里插入图片描述

11.2.课外阅读

以上是关于javaweb实训第六天上午——JSON&SpringMVC进阶的主要内容,如果未能解决你的问题,请参考以下文章

javaweb实训第四天上午——员工管理系统-JavaBean&EL&JSTL&MVC思想

javaweb实训第二天上午——jQuery笔记

javaweb实训第二天上午——jQuery笔记

javaweb实训第三天上午——Servlet

javaweb实训第五天上午——Spring基础

javaweb实训第二天上午——jQuery基础