目录
1.3 ResponseBody响应json数据(含静态资源拦截问题解决方法)
1.3.2 使用示例:使用@ResponseBody注解把JavaBean对象转换成json字符串
2.3.2 演示示例(在上面SpringMVC传统方式文件上传的演示项目上修改)
3.2.4 自定义异常处理器,实现HandlerExceptionResolver接口,编写异常处理逻辑
3.2.5 在springmvc.xml中配置自定义异常处理器
4.2.1 第一步:编写一个普通类实现 HandlerInterceptor 接口
4.2.2 第二步 在springmvc.xml中配置拦截器
第一章:响应数据和结果视图
1.响应数据的返回值分类
1.1 返回值为String类型
- 使用说明
Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。即指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp。项目环境搭建参见上节SpringMVC学习笔记1.
- 具体应用场境
Controller代码
@Controller
@RequestMapping(path = "/user")
public class TestController {
/**
* 请求参数的绑定
*/
@RequestMapping(value="/initUpdate")
public String initUpdate(Model model) {
// 模拟从数据库中查询的数据
User user = new User();
user.setUsername("张无忌");
user.setPassword("065864");
user.setMoney(100d);
user.setBirthday(new Date());
model.addAttribute("user", user);
//指定逻辑视图名
// 根据视图解析器解析为jsp的物理路径/WEB-INF/pages/success.jsp
return "update";
}
}
请求jsp代码
<h3>修改用户</h3>
${ requestScope }
<form action="user/update" method="post">
姓名:<input type="text" name="username" value="${ user.username }"><br>
密码:<input type="text" name="password" value="${ user.password }"><br>
金额:<input type="text" name="money" value="${ user.money }"><br>
<input type="submit" value="提交">
</form>
响应success.jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>跳转成功</title>
</head>
<body>
<h3>${user.username}</h3>
<h3>${user.password}</h3>
</body>
</html>
运行结果:
1.2.返回值为void类型
- 使用说明
我们知道 Servlet 原始 API 可以作为控制器中方法的参数。
1. 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
默认会跳转到@RequestMapping(value="/pathName") 中的pathName的页面。
2. 可以使用请求转发或者重定向跳转到指定的页面
在 controller 方法形参上可以定义 request 和 response,使用 request 或 response 指定响应结果
- 具体的应用场景
@RequestMapping(path = "/testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
//响应方式一:请求转发
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
//响应方式二:重定向
response.sendRedirect(request.getContextPath()+"/index.jsp");
//响应方式三:直接响应数据
//设置中文乱码
response.setCharacterEncoding("utf-8");
//设置浏览器打开解析编码
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json数据");
}
1.3 返回值是ModelAndView对象
- 使用说明
ModelAndView 是 SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。 使用该对象作为返回值的用法类似于返回值类型是String
该对象中有两个方法:
ModelAndView addObject(String attributeName, Object attributeValue),它可用于把数据存储到ModelAndView对象中,底层会把数据存放到request域对象中
void setViewName(@Nullable String viewName) 用于设置逻辑视图名称,视图解析器会根据名称前往指定的视图(即jsp页面)
- 使用示例
/**
* 返回值为ModelAndView
* @return
*/
@RequestMapping(path = "testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setUsername("爞笑");
user.setPassword("065864");
//将user对象存储到modelAndView对象中
modelAndView.addObject("user",user);
//设置跳转到哪个页面
modelAndView.setViewName("success");
return modelAndView;
}
- 具体应用场景
/**
* 返回ModelAndView对象
* 可以传入视图的名称(即跳转的页面),还可以传入对象。
* @return
* @throws Exception
*/
@RequestMapping(value="/findAll")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
// 跳转到list.jsp的页面
mv.setViewName("list");
// 模拟从数据库中查询所有的用户信息
List<User> users = new ArrayList<>();
User user1 = new User();
user1.setUsername("张三");
user1.setPassword("123");
User user2 = new User();
user2.setUsername("赵四");
user2.setPassword("456");
users.add(user1);
users.add(user2);
// 添加对象
mv.addObject("users", users);
return mv;
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!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>
<h3>查询所有的数据</h3>
<c:forEach items="${ users }" var="user">
${ user.username }
</c:forEach>
</body>
</html>
1.2 SpringMVC框架提供的转发和重定向
1.2.1 forward请求转发
controller 方法在提供了 String 类型的返回值之后,默认就是请求转发。
注意:当使用SpringMVC提供的转发关键字时,是无法使用视图解析器的,所以转发路径需要手动写完整
想进行请求转发也可以编写成
/**
* 使用SpringMVC关键字请求转发
* @return
*/
@RequestMapping(path = "testForwardOrRedirect")
public String testForwardOrRedirect(){
System.out.println("testForwardOrRedirect执行了。。。。");
//使用SpringMVC框架提供的转发关键字时是无法使用视图解析器进行地址解析
return "forward:/WEB-INF/pages/success.jsp";
}
1.2.2 redirect重定向
contrller 方法提供了一个 String 类型返回值之后,它需要在返回值里使用:redirect:
想进行重定向也可以编写成
/**
* 使用SpringMVC关键字重定向
* @return
*/
@RequestMapping(path = "testForwardOrRedirect")
public String testForwardOrRedirect(){
System.out.println("testForwardOrRedirect执行了。。。。");
//使用SpringMVC提供的重定向关键字进行页面重定向
//此时SpringMVC底层会自动为添加项目目录补全跳转路径
return "redirect:/index2.jsp";
}
注意:使用此关键字,相当于“response.sendRedirect(url)”。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不能写在 WEB-INF 目录中,否则无法找到。因为重定向相当于重新在浏览器发起一次请求,外界无法直接访问WEB-INF目录下的资源
1.3 ResponseBody响应json数据(含静态资源拦截问题解决方法)
1.3.1 使用说明
@ResponseBody注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的数据如:json,xml 等,通过 Response 响应给客户端。
注意:由于SpringMVC配置了前端控制器DispatcherServlet,DispatcherServlet会拦截到所有的资源,会导致静态资源(img、css、js)也会被拦截,从而不能被使用。
解决问题静态资源拦截问题就是需要配置静态资源不进行拦截,有三种配置方法,详细请参考博客https://blog.csdn.net/Dream_Weave/article/details/84777833
这里介绍其中一种方式:在springmvc.xml配置文件添加如下配置:
<!--配置指定哪些资源不会被前端控制器拦截--> <mvc:resources mapping="/js/**" location="/js/"></mvc:resources> <mvc:resources mapping="/css/**" location="/css/"></mvc:resources> <mvc:resources mapping="/images/**" location="/images/"></mvc:resources>
1.3.2 使用示例:使用@ResponseBody注解把JavaBean对象转换成json字符串
- 需求:
使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端。
- 前置知识点:
Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包,当异步请求时,SpringMVC就会自动去查找这些jar包,json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
注意2.7.0以下的版本用不了
maven依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency>
- 使用ajax发送请求(先导入上面的依赖)
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>测试首页</title>
<script src="js/jquery.min.js"></script>
<script>
//页面加载,添加事件
$(function () {
$("#btn").click(function () {
$.ajax({
//请求方式
type:"post",
//请求路径
url:"user/testAjax",
//发送的数据类型及内容编码
contentType:"application/json;charset=UTF-8",
//发送的数据
data:'{"username":"张三","password":"123456","age":18}',
//预期服务器返回的数据类型
dataType:"json",
//请求成功回调函数
success:function (data) {
alert(data.username+":"+data.password+":"+":"+data.age);
},
//请求失败回调函数
error:function () {
}
});
});
});
</script>
</head>
<body>
<button type="button" id="btn">发送ajax请求</button>
</body>
</html>
- Controller代码,使用@ResponseBody注解把JavaBean对象直接转换成json字符串响应给页面
/**
* 接收Ajax请求
*/
@RequestMapping(path = "/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
System.out.println("testAjax方法执行了。。。");
//在客户端发送ajax请求,是通过请求体传得是json字符串
//后端把json字符串封装到user对象,需要用到@RequestBody注解
System.out.println(user);
//做响应,模拟查询数据库
user.setUsername("哈哈哈");
user.setPassword("123");
user.setAge(90);
//使用@ResponseBody注解,底层会调用方法直接把user对象转成json字符串
return user;
}
- 回顾@RequestBody注解的使用
使用示例一:使用@RequestBody获取请求体数据
- 使用ajax发送异步请求,这里使用JQuery函数
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>测试首页</title> <script src="js/jquery.min.js"></script> <script> //页面加载,添加事件 $(function () { $("#btn").click(function () { $.ajax({ //请求方式 type:"post", //请求路径 url:"user/testAjax", //发送的数据类型及内容编码 contentType:"application/json;charset=UTF-8", //发送的数据 data:'{"username":"张三","password":"123456","age":18}', //预期服务器返回的数据类型 dataType:"json", //请求成功回调函数 success:function (data) { }, //请求失败回调函数 error:function () { } }); }); }); </script> </head> <body> <button type="button" id="btn">发送ajax请求</button> </body> </html>
- Controller代码,使用@RequestBody获取请求体数据
/** * 接收Ajax请求 */ @RequestMapping(path = "/testAjax") public void testAjax(@RequestBody String body){ System.out.println("testAjax方法执行了。。。"); System.out.println(body); }
- 运行结果:
使用示例二: 使用@RequestBody注解把请求体json字符串转换成JavaBean的对象
user实体类
@Component public class User implements Serializable { private String username; private String password; private Integer age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
只需要将上面的Controller代码修改成下面这样:
/** * 接收Ajax请求 * 在客户端发送ajax请求,是通过请求体传得是json字符串 * 后端把json字符串封装到user对象,需要用到@RequestBody注解 */ @RequestMapping(path = "/testAjax") public void testAjax(@RequestBody User user){ System.out.println(user); }
第二章:SpringMVC实现文件上传
2.1 文件上传的回顾
2.1.1 文件上传的必要前提
前端页面要求:
- 第一:form 表单的 enctype 取值必须是:
multipart/form-data (默认值是:application/x-www-form-urlencoded)
enctype:是表单请求正文的类型
- 第二:method 属性取值必须是 Post
- 第三:提供一个文件选择域 <input type="file" />
<form action="user/fileupload" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="upload"/><br/> <input type="submit" value="上传文件"/> </form>
2.1.2 文件上传的原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。
- 当 form 表单的 enctype 取值为enctype=”application/x-www-form-urlencoded”(默认值)时,
form 表单的正文内容是: key=value&key=value&key=value
- 当 form 表单的 enctype 取值为 Mutilpart/form-data 时,
浏览器就会把整个文件表单内容的请求体用分隔符分成若干个部分
请求正文内容就变成: 每一部分都是 MIME 类型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--
后台解析处理:先获取请求体的内容,再一步步解析,最终拿到文件的内容,再写入到指定的文件当中去,最终完成文件的上传。而这部分事情Commons-fileupload 组件已经做好。
2.1.3 传统方式文件上传:借助第三方组件实现
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和 commons-io。commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它 工作时需要 commons-io 包的支持。
maven依赖
<!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
- 上传示例(先按照之前SpringMVC学习笔记1的步骤搭建好SpringMVC的开发环境)
前端jsp代码:使用post方式,设置表单enctype="multipart/form-data"
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<h3>文件上传</h3>
<form action="file/fileUpload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="file">
<input type="submit" value="上传">
</form>
</body>
</html>
Controller代码,文件上传步骤:
第一步:获取上传的位置路径,即上传到哪里
第二步:判断该路径是否存在,若不能存在,则创建
第三步:通过磁盘文件项工厂类获取解析request对象
第四步:解析request获取上传文件项列表
第五步:判断当前item是否是上传文件项,若是,则上传
第六步:删除临时文件。
@RequestMapping(path = "fileUpload1")
public String fileUpload1(HttpServletRequest request) throws Exception {
System.out.println("上传文件。。。");
//使用fileUpload组件完成文件上传
//设置上传的位置
String path = request.getServletContext().getRealPath("/uploads");
//判断该路径是否存在
File file = new File(path);
if(!file.exists()){
//创建该文件夹
file.mkdir();
}
//解析request对象,调用磁盘文件项工厂,获取上传磁盘文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
//获取文件上传对象
ServletFileUpload upload = new ServletFileUpload(factory);
//解析request获取上传文件项列表
List<FileItem> itemList = upload.parseRequest(request);
//遍历
for (FileItem item : itemList) {
//判断当前item对象是否是上传文件项
if(item.isFormField()){
//说明item是普通表单项
}else{
//说明是上传文件项
//获取上传文件的名称
String filename = item.getName();
//重新命名文件名
UUID uuid = UUID.randomUUID();
String header = uuid.toString().replace("-", "");
filename=header+filename;
//完成文件上传,指定上传路径与上传文件名称
item.write(new File(path,filename));
//删除临时文件
/*
如果上传文件大于10kb,则会创建临时文件,当你把文件传完就可以把临时文件删掉
如果上传文件小于10kb,则会在内存中生成缓存文件,缓存文件不需要我们处理
*/
item.delete();
}
}
return "success";
}
注意:
- 如果上传文件大于10kb,则会创建临时文件,当你把文件传完就可以把临时文件删掉
- 如果上传文件小于10kb,则会在内存中生成缓存文件,缓存文件不需要我们处理
2.2 SpringMVC传统方式文件上传
2.2.1 原理分析
- 说明:
传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上。 并且上传完成之后,浏览器可能跳转。
- 原理分析:
文件上传请求request到达前端控制器后,会调用文件解析器解析request对象,返回一个文件上传项对象upload给前端控制器,前端控制器就会通过参数绑定方法把文件上传项对象传到Controller控制器的方法
- 注意:
文件解析器CommonsMultipartResolver依赖于Commons-fileupload 和 commons-io这两个jar包,所以需要在项目中应用导入这两个依赖
2.2.2 示例
在springmvc.xml文件中配置文件解析器CommonsMultipartResolver
<!-- 配置文件解析器对象,id必须为multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--最大上传文件大小-->
<property name="maxUploadSize" value="10485760"></property>
</bean>
前端jsp页面
<fieldset>
<legend></legend>
<h3>SpringMVC方式文件上传</h3>
<form action="file/fileUpload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br>
<input type="submit" value="上传">
</form>
</fieldset>
Controller代码
/**
* SpringMVC文件上传方式
* @return
*/
@RequestMapping(path = "/fileUpload2")
public String fileUpload2(HttpServletRequest request,MultipartFile upload) throws Exception{
System.out.println("SpringMVC文件上传。。。");
//使用fileUpload组件完成文件上传
//设置上传的位置
String path = request.getServletContext().getRealPath("/uploads");
//判断该路径是否存在
File file = new File(path);
if(!file.exists()){
//创建改文件夹
file.mkdir();
}
//说明是上传文件项
//获取上传文件的名称
String filename = upload.getOriginalFilename();
//重新命名文件名
UUID uuid = UUID.randomUUID();
String header = uuid.toString().replace("-", "");
filename=header+filename;
//完成文件上传,指定上传路径与上传文件名称
upload.transferTo(new File(path,filename));
return "success";
}
注意: 文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他 字段也将无法绑定)
2.3 springmvc 跨服务器方式的文件上传
2.3.1 分服务器的目的
在实际开发中,我们会有很多处理不同功能的服务器。
例如:
应用服务器:负责部署我们的应用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
(注意:此处说的不是服务器集群)
sun公司为我们提供了可以跨服务器上传文件的jar包:
maven依赖:
2.3.2 演示示例(在上面SpringMVC传统方式文件上传的演示项目上修改)
- 环境搭建:
准备两个服务器,设置不同的端口号
并在存储图片的tomcat服务器中修改web.xml文件,如下:
加入此行的含义是:接收文件的目标服务器可以支持写入操作。
创建一个用于存放图片的 web maven 工程,并在webapp文件夹下创建uploads文件夹用于存放上传的文件
在我们负责处理文件上传的SpringMVC项目(参照上面Springmvc传统方式文件上传示例)中添加以下依赖:
<!-- 跨服务器上传jar包依赖 --> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.18.1</version> </dependency>
配置文件上传解析器以及jsp代码参见上面案例
编写控制器Controller代码
/**
* SpringMVC文件上传方式:跨服务器上传
* @return
*/
@RequestMapping(path = "/fileUpload3")
public String fileUpload3(MultipartFile upload) throws Exception{
System.out.println("SpringMVC跨服务器文件上传。。。");
//定义上传文件服务器的路径
String path="http://localhost:8081/FileUploadServer_war_exploded/uploads/";
//获取上传文件的名称
String filename = upload.getOriginalFilename();
//重新命名文件名
UUID uuid = UUID.randomUUID();
String header = uuid.toString().replace("-", "");
filename=header+"_"+filename;
//创建客户端对象
Client client = Client.create();
//和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
//上传文件
webResource.put(upload.getBytes());
return "success";
}
第三章:SpringMVC的异常处理
3.1异常处理的思路
- 系统中异常包括两类:非运行时异常(预期异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
- Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进行异常的处理。
如不处理异常,异常会直接抛到浏览器页面上:
3.2 实现步骤
3.2.1 定义异常类和友好错误提示页面
public class SysException extends Exception {
//存储提示信息
private String message;
public SysException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<title>执行失败</title>
</head>
<body>
<h2>执行失败!</h2>
<h3>${message }</h3>
</body>
</html>
3.2.2 自定义异常类
public class SysException extends Exception {
//存储提示信息
private String message;
public SysException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3.2.3 修改Controller代码,模拟抛出异常
/**
* 模拟抛出异常
* @return
*/
@RequestMapping(path = "/testException")
public String testException() throws SysException {
System.out.println("testException方法执行了....");
try {
//模拟异常
int i=10/0;
} catch (Exception e) {
//打印异常信息
e.printStackTrace();
//抛出自定义异常
throw new SysException("查询所有用户出现问题了");
}
return "success";
}
3.2.4 自定义异常处理器,实现HandlerExceptionResolver接口,编写异常处理逻辑
/**
* 自定义异常处理器
* 当异常抛到前端控制器时,前端控制器会查找异常处理器执行其处理方法
* Tool: IntelliJ IDEA.
* Author: Poison
* CreateDateTime: 2020/3/6 16:51
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 异常处理逻辑
* @param request 请求对象
* @param response 响应对象
* @param handler 当前处理器对象
* @param ex 程序抛出的异常对象
* @return
*/
@Override
public ModelAndView resolveException(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) {
//获取异常对象
SysException e=null;
//判断当前异常对象是否是SysException类型
if(ex instanceof SysException){
e=(SysException)ex;
}else{
new SysException("系统正在维护.....");
}
//创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMsg",e.getMessage());
//跳转到有好错误提示页面
modelAndView.setViewName("error");
return modelAndView;
}
}
3.2.5 在springmvc.xml中配置自定义异常处理器
<!--配置异常处理器对象-->
<bean id="sysExceptionResolver" class="cn.poison.exception.SysExceptionResolver"></bean>
运行结果:
点击测试请求
跳转友好提示页面
控制台
第四章:SpringMVC框架中的拦截器
4.1拦截器的概述
- Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
- 用户可以自己定义一些拦截器来实现特定的功能。
- 谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
- 说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但是也有区别,接下来我们就来说说他们的区别:
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
过滤器在 url-pattern 中配置了 /* 之后,可以对所有要访问的资源拦截。
拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
它也是 AOP 思想的具体应用。
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor 接口。
4.2 自定义拦截器步骤
4.2.1 第一步:编写一个普通类实现 HandlerInterceptor 接口
/**
* 自定义拦截器
* Tool: IntelliJ IDEA.
* Author: Poison
* CreateDateTime: 2020/3/6 17:52
*/
public class MyInterceptor implements HandlerInterceptor {
/**
* 预处理,controller方法执行前
* 如果return true,表示放行,执行下一个拦截器,如果没有,执行Controller中的方法
* 如果return false,表示不放行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor执行了。。。。。");
return true;
}
}
4.2.2 第二步 在springmvc.xml中配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--配置具体拦截的方法-->
<!--"/user/*"表示拦截user的Controller下的方法,“/**”表示拦截所有Controller方法-->
<mvc:mapping path="/user/*"/>
<!--不要拦截的方法-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class="cn.poison.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.3 HandlerInterceptor接口中的方法
- 1. preHandle方法是controller方法执行前拦截的方法
1. 可以使用request或者response跳转到指定的页面
2. return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
3. return false不放行,不会执行controller中的方法。
- 2. postHandle是controller方法执行后执行的方法,在JSP视图执行前。
1. 可以使用request或者response跳转到指定的页面
2. 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
- 3. afterCompletion方法是在JSP执行后执行
1. request或者response不能再跳转页面了
注意:当配置多个拦截器时,afterCompletion方法的执行顺序按配置顺序的逆序执行,如先配置拦截器1,再配置拦截器2
他们的执行顺序是:
4.3.1 源码
public interface HandlerInterceptor {
/**
* 如何调用:
* 按拦截器定义顺序调用
* 何时调用:
* 只要配置了都会调用
* 有什么用:
* 如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回 true。
* 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* 如何调用:
* 按拦截器定义逆序调用
* 何时调用:
* 在拦截器链内所有拦截器返成功调用
* 有什么用:
* 在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,
* 在该方法中对用户请求 request 进行处理。
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 如何调用:
* 按拦截器定义逆序调用
* 何时调用:
* 只有 preHandle 返回 true 才调用
* 有什么用:
* 在 DispatcherServlet 完全处理完请求后被调用,
* 可以在该方法中进行一些资源清理的操作。
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
4.4 配置多个拦截器
path="/user/*"表示拦截“user”控制器下的所有方法
path="/**"表示拦截所有控制器下的方法
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 拦截器1 -->
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/user/*"/>
<!-- 哪些方法不进行拦截
<mvc:exclude-mapping path=""/>
-->
<!-- 注册拦截器对象 -->
<bean class="cn.itcast.demo1.MyInterceptor1"/>
</mvc:interceptor>
<!-- 拦截器2 -->
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/**"/>
<!-- 注册拦截器对象 -->
<bean class="cn.itcast.demo1.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>