从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

    • SpringBoot教程-尚硅谷

      • SpringBoot 课程介绍
      • Spring 和 SpringBoot
      • HelloWorld
      • 了解自动配置原理
      • 底层注解-@Configuration 详解
      • 底层注解-@Import 导入组件
      • 底层注解-@Conditional 条件装配
      • 原生配置文件引入-@ImportResource
      • 底层注解-配置绑定 @ConfigurationProperties
      • 自动配置原理
      • 自动配置流程
      • Lombok 简化开发
      • DevTools
      • Spring-Initailizr
      • 配置文件-Yaml 用法
      • Web 开发简介
      • web 开发-静态资源规则于定制化
      • 静态资源配置原理
      • Rest 映射及源码解析
      • 请求映射原理
      • 常用参数注解使用
      • MatrixVariable:矩阵变量
      • 各种类型参数解析原理
      • Servlet-API 参数解析原理
      • Model、Map 参数解析原理
      • 自定义对象参数绑定原理
      • 自定义 Converter 原理
      • 数据响应原理
      • 内容协商原理
      • 基于请求参数的内容原理
      • 自定义 MessageConverter 原理
      • Thymeleaf 初体验
      • web 实验-后台管理系统
      • web 实验-抽取公共页面
      • web 实验-遍历数据
      • 源码分析-视图解析器与视图
      • 拦截器-登录检查与静态资源放行
      • 拦截器的执行时机和原理
        • 以 debug 方式启动
        • preHandle
        • applyPostHandle
        • AfterCompletion
        • 总结
      • 单文件和多文件上传的使用
      • 文件上传原理
      • 错误处理机制
      • 错误处理-底层组件源码分析
      • 异常处理流程
      • 几种异常处理原理
      • Web 原生对象注入
      • 嵌入式 Servlet 容器
      • 定制化原理
      • 数据库场景的自动配置分析和整合测试
      • 自定义方式整合 Druid
      • 通过 starter 整合 Druid
      • 整合 Mybatis
      • 使用注解整合 Mybatis
      • 整合 MybatisPlus 操作数据库
      • MybatisPlus-列表分页展示
      • 整合 Redis
      • 单元测试-Junit5
      • 单元测试-断言机制
      • 单元测试-前置条件
      • 单元测试-嵌套测试
      • 单元测试-参数化测试
      • 指标监控-基本概念
      • 指标监控-配置 EndPoint
      • 指标监控-可视化
      • 原理解析-Profile 功能
      • 配置文件深入
      • 自定义 Starter
      • SpringApplication 初始化过程
      • SpringBoot 完整启动过程
  • Java
  • SpringBoot
  • SpringBoot教程-尚硅谷
2023-08-22
目录

拦截器的执行时机和原理

# 380.拦截器的执行时机和原理

我们来通过断点调试的方式,来分析原理 ‍

# 以 debug 方式启动

我们在 doDispatch 方法和 mainPage 方法上加断点,然后以 debug 方式启动,然后登录 ‍ ps:可以在 IDEA 中先取消所有断点,然后放行请求,再启用断点。先查看所有断点:

‍

然后忽略:

‍

或者直接忽略所有断点:

此时我们会来到 doDispatch 方法:

‍

在执行完 getHandler 方法后,就会获取到执行链(HandlerExecutionChain),并且会包含拦截器链,可以看到有 3 个对象,其中第 0 个是我们自己写的,第 1 和 2 是自带的。

# preHandle

在调用目标方法之前,会先调用 applyPreHandle 方法,也就是调用拦截器的方法;如果拦截器里的校验不通过,就不会放行,直接 return;

如果拦截器校验通过,才会执行目标方法。

在 applyPreHandle 方法中,会拿到所有的拦截器,然后循环调用拦截器的方法:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

‍ 注意,会先顺序执行所有拦截器的 preHandle 方法

如果返回 true,则执行下一个拦截器的 preHandle 方法。

如果返回 false(即有一个拦截器不放行),就会执行 triggerAfterCompletion 方法,该方法会逆序执行被触发过的拦截器的 afterCompletion 方法(注意 i 是递减的):

所有拦截器都放行后,才会执行目标方法

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
	for (int i = this.interceptorIndex; i >= 0; i--) {
		HandlerInterceptor interceptor = interceptors[i];
		try {
			interceptor.afterCompletion(request, response, this.handler, ex);
		}
		catch (Throwable ex2) {
			logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
		}
	}
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

‍ 此外,在处理请求的过程中,如果有异常发生,在 catch 块中也会执行 triggerAfterCompletion 方法:

# applyPostHandle

目标方法执行完后,则会执行 applyPostHandle 方法:

该方法也很简单,就是执行所有拦截器的 postHandle 方法:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {

	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = interceptors.length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = interceptors[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}
1
2
3
4
5
6
7
8
9
10

‍

# AfterCompletion

处理完请求,就是处理返回结果了,也就是执行 processDispatchResult 方法:

‍

该方法内就会执行 triggerAfterCompletion 方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
    //..... 省略其他代码
    if (mappedHandler != null) {
	// Exception (if any) is already handled..
	mappedHandler.triggerAfterCompletion(request, response, null);
    }
}
1
2
3
4
5
6
7
8
9

‍ 不难猜到,也是循环遍历执行每个拦截器的 afterCompletion 方法:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
	for (int i = this.interceptorIndex; i >= 0; i--) {
		HandlerInterceptor interceptor = interceptors[i];
		try {
			interceptor.afterCompletion(request, response, this.handler, ex);
		}
		catch (Throwable ex2) {
			logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
		}
	}
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

‍

# 总结

  1. 根据当前请求,找到 HandlerExecutionChain【可以处理请求的 handler 以及 handler 的所有 拦截器】

  2. 先来顺序执行 所有拦截器的 preHandle 方法

    1. 如果当前拦截器 prehandler 返回为 true,则执行下一个拦截器的 preHandle
    2. 如果当前拦截器返回为 false。直接倒序执行所有已经执行了的拦截器的 afterCompletion 方法
  3. 如果任何一个拦截器返回 false,直接 return,不执行目标方法

  4. 所有拦截器都返回 true,执行目标方法

  5. 倒序执行所有拦截器的 postHandle 方法。

  6. 前面的步骤有任何异常都会直接倒序触发 afterCompletion 方法

  7. 页面成功渲染完成以后,也会倒序触发 afterCompletion 方法 ‍ 示意图:

‍

上次更新: 2025/5/17 12:26:09
拦截器-登录检查与静态资源放行
单文件和多文件上传的使用

← 拦截器-登录检查与静态资源放行 单文件和多文件上传的使用→

最近更新
01
吐槽一下《僵尸校园》
05-15
02
2025 年 4 月记
04-30
03
山西大同 “订婚强奸案” 将会给整个社会带来的影响有多严重? - 知乎 转载
04-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式