Interceptor

拦截器Interceptor的使用及原理

引用:https://blog.csdn.net/qq_45875349/article/details/139187564?fromshare=blogdetail&sharetype=blogdetail&sharerId=139187564&sharerefer=PC&sharesource=m0_51140831&sharefrom=from_link

1. 拦截器的实现

在 Spring Boot 应用程序开发中,拦截器(Interceptor)是一个非常有用的工具。它允许我们在 HTTP 请求到达 Controller 之前或响应离开 Controller 之后执行一些自定义逻辑。拦截器的实现可以分为以下两个步骤:

  1. 创建自定义拦截器:实现HandlerInterceptor接口并重写接口的preHander方法(执行具体方法之前的预处理方法)
  2. 注册定义拦截器:将⾃定义拦截器加⼊ WebMvcConfigureraddInterceptors ⽅法中 (自定义配置类实现WebMvcConfigurer 并且重写addInterceptors方法,重写addInterceptors方法的目的就是将自定义的拦截器注册到项目中,在这个过程中可以配置拦截规则)

1.1 创建自定义拦截器

首先,我们需要创建一个实现 HandlerInterceptor 接口的类。HandlerInterceptor 接口提供了三个方法:

  • preHandle:在请求处理之前调用
  • postHandle:在请求处理之后调用,但在视图渲染之前
  • afterCompletion:在整个请求完成之后调用,通常用于资源清理

下面给出一个登录拦截的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*自定义拦截器 实现HandlerInterceptor接口 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

// 调用目标方法之前执行的方法
// 此方法返回 boolean 类型的值,
// 如果返回的是true 表示(拦截器)验证成功,继续走后续的流程,执行目标方法
// 如果返回的是 false 表示拦截器验证失败,后续的流程和目标方法就不执行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//用户登录判断业务
HttpSession session = request.getSession(false);
if(session != null && session.getAttribute("admin")!=null){
System.out.println("登录了");
// 用户已登录了
return true;
}
//用户没有登录
System.out.println("还没有登录");
response.sendRedirect("/user/login");//可以重定向到系统的登录路由
//response.setStatus(401);//向前端返回相应的状态码 401:没有权限
return false;
}
}

1.2 注册自定义拦截器

在自定义拦截器之后我们还需要将自定义的拦截器注册到系统的配置中

img

  • addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意⽅法(也就是所有⽅法)
  • excludePathPatterns:表示需要排除的 URL
  • addInterceptors方法里可以一次注册多个拦截器,按照拦截器的注册顺序进行拦截,如果前面的拦截器返回false即在前面拦截了,就不会执行后面的拦截器的逻辑

2. 拦截器实现原理

所有的 Controller 执⾏都会通过⼀个调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 控制台的打印信息看出,如下图所示:

img

所有⽅法都会执⾏ DispatcherServlet 中的 doDispatch 调度⽅法,doDispatch 源码如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@SuppressWarnings("deprecation")
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

// 关键部分:验证所有拦截器的preHandle方法是否返回true
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new ServletException("Handler processing failed: " + err, err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

从上述源码可以看出在开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle,而applyPreHandle ⽅法的实现源码如下:

img

从上述源码可以看出,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执⾏拦截器中的 preHandle ⽅法

3. 拦截器链配置

我们只添加了一个拦截器,如果有多个,该如何配置?配置多个后,执行顺序是什么?

preHandle:与配置顺序相同,必定运行

postHandle:与配置顺序相反,可能不运行

afterCompletion:与配置顺序相反,可能不运行。

  • 当配置多个拦截器时,形成拦截器链
  • 拦截器链的运行顺序参照拦截器添加顺序为准
  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

Interceptor
http://example.com/2025/05/17/Interceptor/
作者
Kon4tsu
发布于
2025年5月17日
许可协议