Spring MVC 拦截器interceptor

Spring MVC 过滤器interceptor

  • 拦截器
  • 拦截器实现代码
    • [xml 配置方式](#xml 配置方式)
      • [1. 实现HandlerInterceptor接口](#1. 实现HandlerInterceptor接口)
      • [2. 配置XML文件,声明拦截路径](#2. 配置XML文件,声明拦截路径)
      • [3. 访问路径,触发拦截器](#3. 访问路径,触发拦截器)
    • 配置类方式
      • [1. 实现HandlerInterceptor接口。](#1. 实现HandlerInterceptor接口。)
      • [2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。](#2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。)
      • [3. 测试拦截器效果。](#3. 测试拦截器效果。)
  • [拦截器 vs. 过滤器](#拦截器 vs. 过滤器)

示例代码地址:https://gitee.com/hua5h6m/framework-java/tree/master/spring-mvc-interceptor

拦截器

定义

Spring 拦截器 是 Spring MVC 框架提供的一种强大机制,它允许您在 HTTP 请求进入控制器控制器处理完 请求后以及最终返回给用户前,注入自定义的预处理和后处理逻辑。

概念与工作原理

拦截器的工作流程基于 AOP 的设计思想,它围绕着 控制器方法的执行 进行拦截。

一个完整的拦截器生命周期包含三个关键时机点:

  1. preHandle - 预处理决定是否拦截,返回值为Boolean类型

    • 时机 :在请求被 DispatcherServlet 接收后,但在调用目标 Controller(Handler)方法 之前 执行。
    • 作用 :这是最常用、最重要的方法。常用于:
      • 权限验证:检查用户是否登录,是否有权限访问该接口。
      • 日志记录:记录请求信息,如 URL、IP、参数等。
      • 性能监控:记录请求开始时间。
      • 参数预处理:对请求参数进行统一的格式化或校验。
    • 返回值boolean 类型。
      • true:继续执行流程,调用下一个拦截器或最终的 Controller 方法。
      • false:中断请求,不再继续向下执行。通常在此方法中直接通过 response 返回结果。
  2. postHandle - 后处理

    • 时机 :在 Controller 方法执行 之后 ,但在视图渲染或返回 JSON 之前 执行。
    • 作用
      • 对 Controller 返回的 ModelAndView 对象进行修改(例如,向模型中添加一些全局数据)。
      • 在返回视图前进行一些处理。
    • 注意 :如果 Controller 方法内部发生了异常,此方法将 不会 被调用。
  3. afterCompletion - 请求完成回调

    • 时机 :在整个请求完成 之后 ,即视图渲染完毕或响应已返回给客户端 之后 执行。
    • 作用
      • 资源清理 :如清理 preHandle 中创建的线程局部变量。
      • 性能监控收尾 :计算整个请求的耗时(需要与 preHandle 中记录的开始时间配合)。
      • 记录最终日志
    • 注意 :无论 Controller 方法执行是否成功,此方法 总会被调用 (类似于 try-catch-finally 中的 finally 块)。

执行顺序

Filter.doFilter() -> DispatcherServlet -> Interceptor.preHandle() -> Controller -> Interceptor.postHandle() -> 渲染视图 -> Interceptor.afterCompletion()


多拦截器执行顺序

在Spring MVC中,当存在多个拦截器时,它们的执行顺序由配置的先后顺序决定 。具体来说,在配置类中通过InterceptorRegistry添加拦截器的顺序就是拦截器的执行顺序。或者在XML中,谁先配置谁先执行。

假设我们有两个拦截器:InterceptorAInterceptorB,并且我们先添加InterceptorA,再添加InterceptorB

  • 正常情况下的执行顺序(所有拦截器的preHandle都返回true):
  1. InterceptorA.preHandle()
  2. InterceptorB.preHandle()
  3. 执行控制器方法(Handler)
  4. InterceptorB.postHandle()
  5. InterceptorA.postHandle()
  6. InterceptorB.afterCompletion()
  7. InterceptorA.afterCompletion()

注意:preHandle方法是按照配置 顺序执行的,而postHandleafterCompletion则是按照配置顺序的反向 顺序执行。先进后出原则

优势

  • 核心概念:Spring 拦截器是 Spring MVC 中用于在控制器方法执行前后插入逻辑的组件。
  • 生命周期 :包含 preHandle(最常用)、postHandleafterCompletion 三个关键方法。
  • 主要应用权限校验、日志记录、性能监控、通用参数处理 等横切关注点。
  • 核心优势:将通用的、非业务性的代码从业务控制器中解耦出来,使代码更加清晰、可维护和可复用。

拦截器实现代码

xml 配置方式

1. 实现HandlerInterceptor接口

java 复制代码
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class UserInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler) throws Exception {

        System.out.println("preHandle:handler触发之前");
        return true;
    }

    @Override
    public void postHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle:handler触发之后");
    }

    @Override
    public void afterCompletion(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion:");
    }
}

2. 配置XML文件,声明拦截路径

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.maven" />

    <!--配置控制器映射器和控制器适配器-->
    <mvc:annotation-driven />
    <!--配置静态资源解析-->
    <mvc:default-servlet-handler />

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--第一个拦截器-->
        <mvc:interceptor>
            <!--拦截那些路径:默认全部拦截-->
            <mvc:mapping path="/user/**"/>
            <!--配置不拦截的路径-->
            <mvc:exclude-mapping path="/user/list"/>
            <!--指定拦截器的位置-->
            <bean class="com.maven.interceptor.UserInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

3. 访问路径,触发拦截器

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("user")
public class UserController {
    @GetMapping("info")
    public String info() {
        return "user info";
    }

    @GetMapping("list")
    public String list() {
        return "user list";
    }

    @GetMapping("exception")
    public String exception() {
        int i = 1 / 0;
        return "user exception";
    }
}
  • 访问/user/info,链接返回结果:
java 复制代码
preHandle
postHandle
afterCompletion
  • 访问/user/exception,链接返回结果:
  • 页面报错500,因此postHandler不会触发,但是afterCompletion无论是否出现异常,都会执行。
java 复制代码
preHandle
afterCompletion

配置类方式

1. 实现HandlerInterceptor接口。

  • 通过@Component注解,添加到容器中。
java 复制代码
@Component
public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

2. 配置类实现webMvcConfigurer接口重载addInterceptors方法。

  • 一定不能忘记添加@Configuration注解。
  • 实现接口WebMvcConfigurer 接口,并重载addInterceptors方法。
java 复制代码
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Autowired
    private UserInterceptor userInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInterceptor).addPathPatterns("/user/**");
    }
}

3. 测试拦截器效果。

java 复制代码
@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("info")
    public String info(){
        return "user info"+ new Date();
    }
}
  • 结果输出:
java 复制代码
preHandle
postHandle
afterCompletion

拦截器 vs. 过滤器

这是一个非常常见的面试题,理解它们的区别至关重要。

特性 Spring 拦截器 Servlet 过滤器
出身 Spring 框架 特有的组件。 Servlet 规范 的一部分,任何 Java Web 应用都能用。
依赖 依赖于 Spring 容器。 不依赖于 Spring,只依赖于 Servlet 容器(如 Tomcat)。
作用域 只能拦截 Spring MVC 的请求(即 DispatcherServlet 处理的请求)。 可以拦截 所有 进入容器的请求(包括静态资源、JSP等)。
获取Bean 可以轻松获取和注入 Spring 容器中的其他 Bean(如 Service)。 默认不能,需要通过 SpringBeanAutowiringSupport 等特殊方式。
控制粒度 可以获取到具体的 Handler(控制器方法) 信息。 只能获取到 ServletRequest 和 ServletResponse。
执行时机 DispatcherServlet 内部,Controller 方法执行的前后。 DispatcherServlet 之前。
相关推荐
lkbhua莱克瓦241 小时前
Java入门——Java跨平台的原理
java·开发语言·笔记·github
float_com2 小时前
【java阶段练习】----- 学生管理系统
java
爱笑的源码基地2 小时前
基于Java+Spring Boot、Vue的B/S医院患者随访管理系统源码,支持二次开发,三级随访体系(出院/门诊随访)、智慧云库(表单配置)
java·vue.js·spring boot·源码·程序代码·随访系统·诊后回访
我曾遇到一束光2 小时前
Springboot3.X+security6.5+jdk21
java·开发语言
jtymyxmz2 小时前
1.1.4 Spring的下载及目录结构
java·后端·spring
robch2 小时前
Spring 的 DelegatingFilterProxy 用法
java·后端·spring
tryxr2 小时前
Java 不同创建线程的方式什么时候才可以使用 this 来获取线程的引用
java·开发语言·多线程
代码程序猿RIP2 小时前
【C 语言面试】高频考点深度解析
java·面试·职场和发展
ashane13142 小时前
Springboot 启动过程及源码分析
java·spring boot·后端
caron42 小时前
c++ -- 循环依赖解决方案
java·c++·算法