Spring MVC 拦截器执行时机

2023-11-16

一、准备工作

搭建好Spring MVC环境以后,我们创建一个拦截器:
名为MyInterceptor并实现HandlerInterceptor接口:
在这里插入图片描述
实现接口方法、便于观察我们只在控制台输出对应的方法名:

package com.jd.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("preHandle");
		return true;
		//初始实现方法代码中,此处返回的是false
		//改为true的原因在下面分析时机时会详细说明
	}

	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle");
	}

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion");
	}
}

拦截器创建好以后,我们在spring xml文件中进行启用设置:

<mvc:interceptors >
	<mvc:interceptor>
		<!-- path属性用于限制能够匹配的请求  -->
		<mvc:mapping path="/userinfo/*"/>
		<bean class="com.jd.interceptor.MyInterceptor"></bean>
	</mvc:interceptor>
</mvc:interceptors>

controller方法:
在这里插入图片描述
主页面请求链接:
在这里插入图片描述
目标页面:
在这里插入图片描述
发送请求:
在这里插入图片描述

二、拦截器执行时机

收到请求后DispatcherServlet类对象如下:
执行FrameworkServlet类service方法、
执行HttpServlet类service方法、
执行FrameworkServlet类doGet方法、
执行FrameworkServlet类processRequest方法、
执行DispatcherServlet类doService方法、
(详情参见Spring MVC请求执行过程
执行DispatcherServlet类doDispatch方法、

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);

			/*mappedHandler = getHandler(processedRequest)
			  根据request得到一个HandlerExecutionChain对象
			  包含了mvc模块的拦截器即handlerInterceptor和真正处理请求的handler*/
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (logger.isDebugEnabled()) {
					logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
				}
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}
			
			/*1、调用applyPreHandle方法,可见此if分支在下面语句之前
			  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			  所以此方法是在执行controller方法前执行*/
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			/*经过applyPreHandle方法(和拦截器preHandle方法)后
			  执行handle方法,完成controller方法
			 (这一行执行完我们会看到控制台输出controller)*/
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			applyDefaultViewName(processedRequest, mv);
			/*2、完成controller方法后,调用applyPostHandle方法,可见此方法在语句
			      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
			     前执行*/
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		/*执行完applyPostHandle方法(和拦截器postHandle方法)后
		  调用processDispatchResult方法*/
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	catch (Exception ex) {
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	}
	catch (Throwable err) {
		triggerAfterCompletion(processedRequest, response, mappedHandler,
		new NestedServletException("Handler processing failed", err));
	}
	finally {
		if (asyncManager.isConcurrentHandlingStarted()) {
			if (mappedHandler != null) {			
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}
/*跳转自doDispatch方法
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
---------------------------------------*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
	boolean errorView = false;
	if (exception != null) {
		if (exception instanceof ModelAndViewDefiningException) {
			logger.debug("ModelAndViewDefiningException encountered", exception);
			mv = ((ModelAndViewDefiningException) exception).getModelAndView();
		}
		else {
			Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
			mv = processHandlerException(request, response, handler, exception);
			errorView = (mv != null);
		}
	}
	if (mv != null && !mv.wasCleared()) {
		/*执行render方法,可见render方法是在执行完
		  applyPostHandle方法(和拦截器postHandle方法)后执行。
		  这一步执行完,响应的视图将处理完毕*/
		render(mv, request, response);
		if (errorView) {
			WebUtils.clearErrorRequestAttributes(request);
		}
	}
	else {
		if (logger.isDebugEnabled()) {
			logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
					"': assuming HandlerAdapter completed request handling");
		}
	}

	if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
		return;
	}

	if (mappedHandler != null) {
		/*3、在render方法执行完后,执行triggerAfterCompletion方法*/
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

1、执行HandlerExecutionChain类中的applyPreHandle方法

/*跳转自doDispatch方法
   if(!mappedHandler.applyPreHandle(processedRequest, response))
--------------------------------------------------*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		//遍历拦截器
		for (int i = 0; i < interceptors.length; i++) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			/*此时interceptor即为MyInterceptor类上转型对象
			  调用preHandle时出现多态现象
			  执行我们MyInterceptor类中的preHandle方法,并返回一个布尔类型结果
			 (这一行执行完我们就会看到控制台输出preHandle)*/
			if (!interceptor.preHandle(request, response, this.handler)) {
				triggerAfterCompletion(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
	}
	return true;
}

可见

  • 根据代码分析如果我们返回false,applyPreHandle方法if(!interceptor.preHandle(request, response, this.handler))为true,则进入if分支最后返回false,则doDispatch方法中if(!mappedHandler.applyPreHandle(processedRequest, response))判断为true进入if分支将直接结束doDispatch方法。
  • 所以我们可以根据需要进行处理,设置返回的值。
  • 由上述:preHandle在Handler Method之前被调用,常用于实现权限。

2、执行HandlerExecutionChain类中的applyPostHandle方法

/*跳转自doDispatch方法
   mappedHandler.applyPostHandle(processedRequest, response, mv);
--------------------------------------------------*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = interceptors.length - 1; i >= 0; i--) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			/*此时interceptor即为MyInterceptor类上转型对象
			  调用postHandle时出现多态现象
			  执行我们MyInterceptor类中的postHandle方法
			 (这一行执行完我们就会看到控制台输出postHandle)*/
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}

可见

  • postHandle方法在controller方法执行后,processDispatchResult方法(和render)方法执行前执行
  • controller方法执行后的视图将传入postHandle
  • 由上述,postHandle方法常用于对请求域中的属性或视图做出修改

3、执行HandlerExecutionChain类中的triggerAfterCompletion方法

/*跳转自processDispatchResult方法
   mappedHandler.triggerAfterCompletion(request, response, null);
--------------------------------------------------*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
	throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			try {
				/*此时interceptor即为MyInterceptor类上转型对象
			      调用afterCompletion时出现多态现象
			  	  执行我们afterCompletion类中的afterCompletion方法
			 	 (这一行执行完我们就会看到控制台输出afterCompletion)*/
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
}

可见

  • afterCompletion方法在render方法执行完以后执行
  • 此时响应的视图已经处理完毕
  • 所以:afterCompletion方法常用于释放资源

至此,拦截器方法执行完毕

三、结果

响应页面
在这里插入图片描述
控制台输出:
在这里插入图片描述
输出顺序即为对应方法执行顺序

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring MVC 拦截器执行时机 的相关文章

  • 你得学会并且学得会的Socket编程基础知识

    这一篇文章 我将图文并茂地介绍Socket编程的基础知识 我相信 如果你按照步骤做完实验 一定可以对Socket编程有更好地理解 本文源代码 可以通过这里下载 http files cnblogs com chenxizhang Socke
  • xLua游戏中的热更新(基于Unity2018)

    什么是热更新 热 就是刚出炉 简单来说就是当游戏某个功能出现bug 或者修改了某个功能 或者增加了某个功能的时候 我们不需要重新下载安装安装包 就可以更新游戏内容 热更新的好处 不用浪费流量重新下载 不用通过商店审核更加快速 不用重新安装玩
  • STM32+M5311对接 OneNET 项目记录

    以前做过的一个演示项目 一款判断人体进出的语言播报方案 通过LwM2M 协议连接 OneNET 硬件平台 M5311 STM32F103 云平台 中国移动 OneNET 语音芯片 WT 唯创知音 WT588D 传感器探头 SHARP 夏普
  • java中String类型转Map类型

    import com alibaba fastjson String str HashMap hashMap JSON parseObject str HashMap class
  • 生活是一种习惯

    生活是一种习惯 昨天看到一天文章 贫穷的理由 让我想到很多 我从家乡出来 来到北京 根据自己的亲身体会 人要养成一个好的习惯 善于学习 不安于现状的习惯 人活着 要想活出个样了 非大众化的人生 就要不安于现状 不要让自己养成满足的习惯 要不
  • [1048]python base64与hex相互转换

    base64转hex coding utf 8 Python 2 import base64 text woidjw b64 hex base64 b64decode text encode hex print b64 hex b64 he
  • C语言学习

    目录 调试 基本概念 bug 调试 debug 调试步骤 Debug和Release VS是集成开发环境 IDE 调试的快捷键 调试窗口 监视 减少程序的错误 assert 表达式 const 变量 调试 基本概念 bug 虫子 bug引申
  • 梦之光芒ctf小游戏闯关过程

    梦之光芒ctf游戏闯关 简介 玩这个游戏 您需要有JS 编码解码 XSS SQL注入 图片隐写 逆向分析等基本常识 游戏地址 http monyer com game game1 进入第1关 入口提示 请点击链接进入第1关 连接在左边 连接
  • max_binlog_size

    max binlog size 默认就是一个G最大值 但是有有什么会发现超过了一个G 原因就是 If a write to the binary log causes the current log file size to exceed
  • java application.yml 配置对象数组

    java application yml 配置对象数组 application yml 配置对象数组 常规对象中获取属性 场景 application yml 配置对象数组 定义配置文件结构 用于定义配置文件的数据结构 打印服务中用到的打印
  • Thinkpad在linux(ubuntu)下修改电池充电阈值,成功解决Thinkpad在Linux下的电池充电问题

    look this for more info http www thinkwiki org wiki Tp smapi 安装tp smapi aptitude install tp smapi dkms modprobe tp smapi
  • Kubernetes弃用Docker的由来和始末

    2020年12月初 Kubernetes在发布v1 20的时候重磅宣称将逐渐弃用Docker 一石激起千层浪 瞬间引爆容器圈 但没想到已经过去两个月时间了 还有文章用UC体误导吃瓜群众 还在学Docker Docker已死 额 累了 毁灭吧
  • mysql join 自己_用JOIN自己更新MySql

    HI我有查詢選擇了主鍵 id 1或外鍵 1的所有行 這是自己的連接 用JOIN自己更新MySql 選擇 SELECT f2 wz AS wz FROM d7x6r magazyn faktura zakupowa f LEFT JOIN S
  • 强化学习——基本概念

    什么是强化学习 强化学习关注与智能体 agent 如何与环境交互中不断学习以完成特定的目标 与有监督学习相比 不需要告诉智能体数据以及对应的标签 学习相应的模型 而是需要智能体在环境中一次次学习 哪些数据对应哪些标签 从而学习规律知道策略
  • Oracle 数据导入*.sql 提示ORA-01950

    今天执行远程Oracle 数据库数据导入时 提示ORA 01950 超出导入文件大小限制 cmd 远程连接oracle 数据库 sqlplus root root1234 192 50 68 246 orcl 导入指定位置的 sql文件 E
  • 双向广度优先搜索(介绍)

    双向广度优先搜索 广度优先搜索遵循从初始结点开始一层层扩展直到找到目标结点的搜索规则 它只能较好地解决状态不是太多的情况 承受力很有限 如果扩展结点较多 而目标结点又处在较深层 采用前文叙述的广度搜索解题 搜索量巨大是可想而知的 往往就会出
  • http请求 405错误

    http请求 405错误 方法不被允许 Method not allowed 405错误常常伴随着POST请求 所有有人会告诉你这些 但是时候他并不能解决你的问题 所以我说一点不一样的 假如你有一个user类 里面有两个属性userName
  • nat技术简介(转载)

    NAT Network Address Translation 网络地址转换 是将IP数据报文头中的IP地址转换为另一个IP地址的过程 在实际应用中 NAT主要用于实现私有网络访问公共网络的功能 这种通过使用少量的公网IP地址代表较多的私网
  • 快速搭建你的api数据交易平台-图文开发教程

    项目背景 如果你需要开发搭建自己的api数据交易平台 并且能在平台上面进行对客户管理 接口管理 套餐管理 账单管理 充值管理 那么下面将来介绍如何使用接口大师这个框架快速进行开发 安装 PhalApi专业版的运行环境要求如下 操作系统 Wi
  • nVidia TK1 基于深度学习框架 Caffe 的物体识别

    By Toradex 胡珊逢 1 简介 深度学习目前正吸引着越来越多人的关注 相关算法框架层出不穷 例如TensorFlow Caffe Keras CNTK Torch7等等 这些算法在数据分析 聚类 识别和预测方面提供了极大的帮助 因此

随机推荐

  • Python爬虫-某网酒店数据

    前言 本文是该专栏的第5篇 后面会持续分享python爬虫案例干货 记得关注 本文以某网的酒店数据为例 实现根据目标城市获取酒店数据 具体思路和方法跟着笔者直接往下看正文详细内容 附带完整代码 正文 地址 aHR0cHM6Ly93d3cuY
  • 基于核概念的KCCA算法

    基于核概念的KCCA算法 1 由CCA算法过渡至KCCA算法 2 KCCA算法的原理与推导 1 由CCA算法过渡至KCCA算法 典型相关分析 CCA 算法是一种标准的统计技术 用于寻找两个最大相关的随机向量的线性投影 CCA算法是一个计算两
  • 字符串初始化赋值

    在C语言中 字符串是当做字符数组来处理的 所以字符串有两种声明方式 一种是字符数组 一种是字符指针 1 直接逐个初始化字符数组 字符数组的初始化 最容易理解的方式就是逐个字符赋给数组中各元素 char str 10 I a m h a p
  • 单片机毕设项目分享 基于stm32的智能电子秤系统 - 物联网 嵌入式 单片机

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 设计原理 4 1 STM32F103C8T6 4 2 HX711压力传感器 5 部分核心代码 6 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目
  • Linux下安装jre

    原文链接 https blog csdn net qq 34368587 article details 79559102 个人收藏教程 侵权联系我删除 现需要项目部署到Linux中 需要配置java运行环境 注 以下测试环境系统为cent
  • 我看Java虚拟机(2)---Java虚拟机内存区域详解

    虚拟机内存区域的组成 直接上图 程序计数器 对于Java方法 用来选取下一条要执行的字节码 对于本地方法 值为空 线程独有 虚拟机栈 执行Java方法 每一层都是一个栈帧 栈帧包括局部变量表 操作数栈 动态链接和方法出口等信息 线程独有 本
  • Vue使用 dhtmlx-gantt 甘特图

    使用心得和一些坑分享出来 下载 npm install dhtmlx gantt save 创建 ganttVue 组件
  • React之生命周期-setState

  • supervisor入门教程

    supervisor是什么 是一个客户端 服务器系统 允许其用户在类UNIX操作系统上控制许多进程 官方解释 简单点来讲 就是一个监控脚本运行的工具 不过他可以统一化管理 laravel的队列文档上也有相关使用方式方法 例如 定时脚本的启动
  • YUV420数据格式详解

    YUV简介 YUV格式有两大类 planar和packed 对于planar的YUV格式 先连续存储所有像素点的Y 紧接着存储所有像素点的U 随后是所有像素点的V 对于packed的YUV格式 每个像素点的Y U V是连续交叉存储的 YUV
  • 无盘游戏服务器软件,安网卫士

    2018年10月12号更新说明 请注意此版本无后台 需要注册号及收银编码的请联系客服 服务端 1 更改默认备份目录 2 当客户机无SSD施工时在BV进行显示无硬盘 3 添加游戏时 支持拖动 4 删除游戏时 取消 删除客户机文件 选项 5 取
  • gcc中-c和-o参数

    c和 o都是gcc编译器的可选参数 c表示只编译 compile 源文件但不链接 会把 c或 cc的c源程序编译成目标文件 一般是 o文件 o用于指定输出 out 文件名 不用 o的话 一般会在当前文件夹下生成默认的a out文件作为可执行
  • 次表面散射

    专题介绍 在实时渲染和离线渲染领域 对场景模型表面以及空间介质的精细化建模是增加场景真实感的重要手段 计算机图形学领域的许多科研工作者设计出一系列复杂精巧的技术理论 模拟出光线从宏观世界到微观粒子的变化规律 本期专题精选了近年来关于微表面模
  • Android 11 Activity启动流程分析

    Android 11 Activity启动流程分析 本片文章是基于Android 11版本来分析应用Activity的启动的 Activity是Android四大组件中最重要的一个 因为我们所有的页面基本上都是基于Activity开发的 所
  • Spring Data JPA 讲解大全

    https yangbingdong com 2019 spring boot data jpa learning
  • Spring Boot干货系列:(七)默认日志logback配置解析

    原本地址 Spring Boot干货系列 七 默认日志logback配置解析博客地址 tengj top 前言 今天来介绍下Spring Boot如何配置日志logback 我刚学习的时候 是带着下面几个问题来查资料的 你呢 如何引入日志
  • Window 能访问某些网站,不能访问一些网的解决方法

    转自 http blog sina com cn s blog 53dd443a01014pfn html 这几天遇到一个奇怪的事 一台电脑能上google 也能上baidu 就是不能上微软 ping 微软也不行 当然 这个肯定不是公司防火
  • java附近的人_es6.2.4学习----java实现附近搜索(附近的人)

    阅读本文需先了解es对地理位置的处理 本文讲述java代码实现搜索附近的人的功能 第一步 创建可存储地理位置信息的索引 public static void createIndex throws IOException RestHighLe
  • 【Xilinx AX7103 MicroBalze学习笔记7】MicroBlaze AXI4 接口之 DDR 读写实验

    目录 AXI4 协议介绍 实验任务 硬件设计 Vivado 部分 自定义 IP MicroBlaze 配置 配置 PLL IP 配置 MIG IP 添加源文件 IP 软件设计 SDK 部分 lt
  • Spring MVC 拦截器执行时机

    一 准备工作 搭建好Spring MVC环境以后 我们创建一个拦截器 名为MyInterceptor并实现HandlerInterceptor接口 实现接口方法 便于观察我们只在控制台输出对应的方法名 package com jd inte