前言
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, ainterceptor 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(tokeninterceptor).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.cinterceptor - 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-715ada0abaf74a959414802237aaa5c4] o.a.b.interceptor.binterceptor - binterceptor posthandle [info ] [2025-12-03 23:03:11.792] [http-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简单理解成后置逻辑,按照反向的顺序执行。

四、过滤器 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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论