Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

本文主要是介绍Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt...

前言

Spring Boot 中,拦截器通常用于在请求处理前后执行一些逻辑,这点与Servlet容器提供的过滤器有点类似。平常我们开发Jave Web应用的时候,经常能碰到要对请求访问进行通用逻辑处理的场景,比方说记录访问日志、确认访问权限等。你是否曾经纠结于是选过滤器还是拦截器来实现逻辑代码的编写,本篇文章先重点介绍Spring中的interceptor拦截器, 然后就过滤器和拦截器这两个常见但易混淆的请求拦截工具,讨论两者相似且不同之处。

关于过滤器的内容在这篇里有详细介绍链接: 【Java Web】过滤器的核心原理、实现与执行顺序配置

一、核心功能

Interceptor的核心能力来自于HandlerInterceptor这个接口,这是Spring MVC提供的接口。接口主要包含三个待实现的方法,preHandle(),postHandle()和afterCompletion()。

  • preHandle() :在Controller方法执行前调用,返回 false 会中断请求。拦截器最常见实现的方法,一般用来做权限校验、参数预处理等。
  • postHandle():Controller 执行完但视图未渲染前调用,一般用于修改ModelAndView,比如添加一些通用的响应头或修改返回数据。
  • afterCompletion():整个请求完成后调用,可以用来记录日志。

通过这三个方法,就能赋予我们对于Controller层的精细化的控制。如果是WebApi这种项目忽略postHandle方法。

方法里包含各类参数,比如都包含HttpServletRequest和HttpServletResponse变量,用于获取和设置读取请求数据和响应输出。preHandle() 和postHandle()方法的handler参数代表当前 HTTP 请求所匹配的处理器对象,是一个封装了Controller方法的对象、所属类、参数、注解等详细信息的类。postHandle()的modelAndView 则是处理器方法执行完成后返回的模型视图对象。最后afterCompletion方法的可空的Exception参数,如果方法运行出现错误了,那么这个参数就包含具体的异常信息。

Interceptor本质上是就是对Controller层的控制。执行顺序是preHandle => Controller => postHandle => afterCompletion

完整接口

package org.springframework.web.servlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
public interface HandlerInterceptor {
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}

二、拦截器的实现

2.1 定义自定义拦截器

实现拦截器需要定义一个类来实现Interceptor接口,比如我要实现一个验证Jwt的拦截器,编写一个类实现Interceptor接口,并且我们在类上用@Component注解修饰,将其当作一个bean对象方便下一步注册。由于Jwt拦截器只需要在请求前进行逻辑验证,这里就只重载preHandle方法。

在preHandle方法里,如果返回值是false,那么后续Controller里的Handler Method是不会执行的,只有返回值是true,后续的Controller才会执行。

package org.araby.blognovelink.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.araby.blognovelink.utils.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String originToken = request.getHeader("Authorization");
        if (originToken == null || originToken.isEmpty()){
            log.info("未登录");
            response.setStatus(401);
            return false;
        }
        String[] parts = originToken.split(" ");
        if (parts.length != 2 || !"Bearer".equalsIgnoreCase(parts[0])) {
            log.info("Token格式错误");
            response.setStatus(401);
            return false;
        }
        String token = parts[1];
        if (token == null || token.isEmpty()){
            log.info("未登录");
            response.setStatus(401);
            return false;
        }
        try {
            JwtUtils.pareseToken(token);
        }
        catch (Exception e){
            log.info("登录失败");
            response.setStatus(401);
            return false;
        }
        log.info("登录成功");
        return true;
    }
}

2.2 注册拦截器

注册拦截器需要定义一个配置类实现WebMvcConfigurer接口。最为关键的是通过addInterceptors重载方法,将我们自定义的拦截器通过添加到InterceptorRegistry中。

registry.addInterceptor()的方法参数便是我们的拦截器,我们还可以在后面通过链式调用添加addPathPatterns【拦截器匹配路径】和excludePathPatterns【排除路径】

@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final TokenInterceptor tokenInterceptor;
    @Autowired
    public WebConfig(TokenInterceptor tokenInterceptor) {
        this.tokenInterceptor = tokenInterceptor;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPatterns("/login/**");
    }
}

三、多拦截器的执行顺序

前面在介绍注册拦截器的时候我们提到过registry.addInterceptor()这个方法,其实它后面还能通过链式调用添加进行设置order(),方法内可以添加参数,越小越先执行。

若未显式设置 order,拦截器执行顺序为注册顺序,也就是registry.addInterceptor的先后

下面我这次三个拦截器,并且让CInterceptor的顺序最靠前,其实BInterceptor ,最后AInterceptor,并观察执行日志。

@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final TokenInterceptor tokenInterceptor;
    private final AInterceptor aInterceptor;
    private final BInterceptor bInterceptor;
    private final CInterceptor cInterceptor;
    @Autowired
    public WebConfig(TokenInterceptor tokenInterceptor, AInterceptophpr aInterceptor, BInterceptor bInterceptor, CInterceptor cInterceptor) {
        this.tokenInterceptor = tokenInterceptor;
        this.aInterceptor = aInterceptor;
        this.bInterceptor = bInterceptor;
        this.cInterceptor = cInterceptor;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //registry.addInterceptor(topythonkenInterceptor).addPathPatterns("/**").excludePathPatterns("/login/**");
        registry.addInterceptor(aInterceptor).addPathPatterns("/**").order(3);
        registry.addInterceptor(bInterceptor).addPathPatterns("/**").order(2);
        registry.addInterceptor(cInterceptor).addPathPatterns("/**").order(1);
    }
}

打印日志很有意思,preHandle方法按照order的顺序是C-B-A,但是postHandle和afterCompletion是按照A-B-C。

[INFO ] [2025-12-03 23:03:11.669] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.CIntewww.chinasem.cnrceptor - CInterceptor preHandle
[INFO ] [2025-12-03 23:03:11.669] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.BInterceptor - BInterceptor preHandle
[INFO ] [2025-12-03 23:03:11.669] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.AInterceptor - AInterceptor preHandle
[INFO ] [2025-12-03 23:03:11.792] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.AInterceptor - AInterceptor postHandle
[INFO ] [2025-12-03 23:03:11.792] [http-nio-9090-exec-2] [REQ-715ada0abafhttp://www.chinasem.cn74a959414802237aaa5c4] o.a.b.interceptor.BInterceptor - BInterceptor postHandle
[INFO ] [2025-12-03 23:03:11.792] [www.chinasem.cnhttp-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.CInterceptor - CInterceptor postHandle
[INFO ] [2025-12-03 23:03:11.792] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.AInterceptor - AInterceptor afterCompletion
[INFO ] [2025-12-03 23:03:11.792] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.BInterceptor - BInterceptor afterCompletion
[INFO ] [2025-12-03 23:03:11.792] [http-nio-9090-exec-2] [REQ-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.CInterceptor - CInterceptor afterCompletion

其实这不难理解,我们可以类似将这三个拦截器当作先进后出的栈结构。preHandle作为Controller的Handler Method的前置逻辑,按照正向的顺序执行。postHandle和afterCompletion简单理解成后置逻辑,按照反向的顺序执行。

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

四、过滤器 VS 拦截器

先说结论,过滤器和拦截器的作用范围不一样。前者是Java EE标准中Servlet规范提供的功能,它能处理任何进入Servlet容器的请求。比如静态资源,http请求,都能通过过滤器拦截。拦截器是Spring MVC提供的功能,它的作用范围属于Spring容器,可以简单理解成拦截器是对Controller层的拦截。如果用一个请求管道类比,过滤器能从管道最开始进行拦截,而拦截器只能到等待请求管道执行到Spring中才能开始拦截。

最核心的差异是过滤器基于Servlet 容器、拦截器基于 Spring 容器;过滤器只能在请求前后操作,拦截器可深入到 Controller方法执行环节

本文讨论的过滤器和拦截器很类似ASP.NET Core中的中间件与过滤器,前者是作用于整个请求管道,后者是作用于Spring/ASP.NET Core框架,可以说是同种思想在不同框架中思想。如果有ASP.NET Core开发背景的小伙伴可以类比去理解

到此这篇关于Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别的文章就介绍到这了,更多相关SpringBoot Interceptor内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156354

相关文章

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程

JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)

《JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)》:本文主要介绍如何在IntelliJIDEA2020.1中创建和部署一个JavaWeb项目,包括创建项目、配置Tomcat服务... 目录简介:一、创建项目二、tomcat部署1、将tomcat解压在一个自己找得到路径2、在idea中添加

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

springboot的controller中如何获取applicatim.yml的配置值

《springboot的controller中如何获取applicatim.yml的配置值》本文介绍了在SpringBoot的Controller中获取application.yml配置值的四种方式,... 目录1. 使用@Value注解(最常用)application.yml 配置Controller 中

springboot中配置logback-spring.xml的方法

《springboot中配置logback-spring.xml的方法》文章介绍了如何在SpringBoot项目中配置logback-spring.xml文件来进行日志管理,包括如何定义日志输出方式、... 目录一、在src/main/resources目录下,也就是在classpath路径下创建logba