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()方法。最终都会给出响应。

相关推荐
曾令胜2 小时前
excel导出使用arthas动态追踪方法调用耗时后性能优化的过程
spring·性能优化·excel
.格子衫.2 小时前
Spring Boot 原理篇
java·spring boot·后端
多云几多2 小时前
Yudao单体项目 springboot Admin安全验证开启
java·spring boot·spring·springbootadmin
摇滚侠4 小时前
Spring Boot 3零基础教程,Spring Intializer,笔记05
spring boot·笔记·spring
2301_768350234 小时前
Vue第二期:组件及组件化和组件的生命周期
前端·javascript·vue.js
Jabes.yang5 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
聪明的笨猪猪5 小时前
Java Redis “高可用 — 主从复制”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
兮动人5 小时前
Spring Bean耗时分析工具
java·后端·spring·bean耗时分析工具
MESSIR225 小时前
Spring IOC(控制反转)中常用注解
java·spring
摇滚侠5 小时前
Spring Boot 3零基础教程,Demo小结,笔记04
java·spring boot·笔记