SpringMVC拦截器

1 拦截器概述

	SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定
的用户请求,并进行相应的预处理与后处理。其拦截的时间点在"处理器映射器根据用户提
交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,
在处理器适配器执行处理器之前"。当然,在处理器映射器映射出所要执行的处理器类时,
已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

2 一个拦截器的执行

自定义拦截器,需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:

➢ preHandle(request,response, Object handler):

该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方

法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。

➢ postHandle(request,response, Object handler,modelAndView):

该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。

由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修

改处理器方法的处理结果数据,且可以修改跳转方向。

➢ afterCompletion(request,response, Object handler, Exception ex):

当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有

工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此

时对 ModelAndView 再操作也对响应无济于事。

afterCompletion 最后执行的方法,清除资源,例如在 Controller 方法中加入数据

2.1 代码

(1)dispatvherServlet配置文件
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.hkd.springmvc.controller"/>

<!--    注册拦截器-->
<!--    <mvc:mapping/>用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所-->
<!--    有请求。-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.hkd.springmvc.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>



</beans>
(2)拦截器类
java 复制代码
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("正在执行MyInterceptor的-------------preHandle()");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("正在执行MyInterceptor的-------------postHandle()");
        modelAndView.addObject("name","tim");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("正在执行MyInterceptor的-------------afterCompletion()");
        HttpSession session = request.getSession();
        Object attr = session.getAttribute("attr");
        System.out.println(attr);
        session.removeAttribute("attr");
        System.out.println(session.getAttribute("attr"));
    }
}
(3)控制器类
java 复制代码
@Controller
public class MyController {

    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView doSome(String name, Integer age, HttpSession session){
        System.out.println("正在执行myController的方法");
        ModelAndView mv = new ModelAndView();
        mv.addObject("name",name);
        mv.addObject("age",age);
        mv.setViewName("view.jsp");
        session.setAttribute("attr","session中的数据");
        return mv;
    }
}
(4)index.jsp请求页
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/";
%>
<html>
<head>
    <title>主页</title>
    <base href="<%=path%>">
</head>
<body>
<form action="some.do" method="post">
    姓名:<input type="text" name="name"><br>
    年龄:<input type="text" name="age"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>
(5)view.jsp展示页面代码
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>view</title>
</head>
<body>
${name}<br>
<hr>
${age}<br>
<hr>
${pageContext.session.getAttribute("attr")}
</body>
</html>

运行预期:

展示页面第一行是tim,虽然传入的是tom,但是MyInterceptor 的postHandle()方法将name更改为了tim。

第二行正常显示前端页面输入的年龄

第三行正常显示"session中的数据",因为afterCompletion()是在中央调度器渲染(数据填充)了响应页面之后执行的,因此上面代码中afterCompletion()的removeAttribute()还没有生效,控制台第一次正常显示"attr"中的内容,第二次为null;

(6)前端输入界面

运行结果

控制台输出
(7)收获
	深刻理解
	afterCompletion(request,response, Object handler, Exception ex):
	当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有
	工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此
	时对 ModelAndView 再操作也对响应无济于事。

(图源:动力节点)

3 多个拦截器的执行

再定义一个拦截器
java 复制代码
public class MyInterceptor2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("正在执行MyInterceptor222的-------------preHandle()");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("正在执行MyInterceptor222的-------------postHandle()");
        modelAndView.addObject("name","tim");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("正在执行MyInterceptor222的-------------afterCompletion()");
    }
}
注册第二个拦截器
xml 复制代码
<!--    注册拦截器-->
<!--    <mvc:mapping/>用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所-->
<!--    有请求。-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.hkd.springmvc.interceptor.MyInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.hkd.springmvc.interceptor.MyInterceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>
执行程序,运行结果

结论

当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再

次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专

门的方法栈中放入该拦截器的 afterCompletion()方法。

图源:动力节点

从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,

其后续的处理器方法与 postHandle()方法将无法执行。但,无论执行链执行情况怎样,只要

方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的

afterCompletion()方法。最终都会给出响应。

相关推荐
Marry1.03 分钟前
uniapp背景图用本地图片
前端·uni-app
2401_854391086 分钟前
城镇住房保障:SpringBoot系统功能概览
java·spring boot·后端
hummhumm7 分钟前
Oracle 第29章:Oracle数据库未来展望
java·开发语言·数据库·python·sql·oracle·database
夏河始溢9 分钟前
一七八、Node.js PM2使用介绍
前端·javascript·node.js·pm2
记忆深处的声音9 分钟前
vue2 + Element-ui 二次封装 Table 组件,打造通用业务表格
前端·vue.js·代码规范
陈随易10 分钟前
兔小巢收费引发的论坛调研Node和Deno有感
前端·后端·程序员
wainyz16 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊22 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
熊的猫25 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
lzb_kkk30 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee