从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • 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完整启动过程
      • SpringBoot
  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • 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 方法

‍

示意图:

​​

‍

在GitHub上编辑此页 (opens new window)
上次更新: 2023/8/23 10:20:25
拦截器-登录检查与静态资源放行
单文件和多文件上传的使用

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

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式