Spring MVC 源码分析之 DispatcherServlet#getHandlerAdapter 方法
前言:
前面我们分析了 Spring MVC 的工作流程源码,其核心是 DispatcherServlet#doDispatch 方法,我们前面分析了获取 Handler 的方法 DispatcherServlet#getHandler 方法,本篇我们重点分析一下获取当前请求的适配器 HandlerAdapter 的实现原理,具体方法入口是 DispatcherServlet#getHandlerAdapter。
Spring MVC 知识传送门:
详解 Spring MVC(Spring MVC 简介)
Spring MVC 初始化源码分析
Spring MVC 工作流程源码分析
Spring MVC 源码分析之 DispatcherServlet#getHandler 方法
** DispatcherServlet#getHandlerAdapter方法源码分析**
DispatcherServlet#getHandlerAdapter方法就是从 handlerAdapters 中查询匹配当前请求的 Handler,只要找到了就不在循环直接返回,我们我们重点关注adapter.supports(handler) 这行代码,这里实际调用的是接口的抽象类 AbstractHandlerMapping 中的 getHandler 方法,下面接着分析。
//org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {this.handlerAdapters 为空判断 DispatcherServlet 初始化时注册的 handlerAdaptersif (this.handlerAdapters != null) {//迭代遍历Iterator var2 = this.handlerAdapters.iterator();while(var2.hasNext()) {HandlerAdapter adapter = (HandlerAdapter)var2.next();//找到匹配当前 handler的 adapterif (adapter.supports(handler)) {//找到就返回return adapter;}}}//如果最后都没找到 抛出异常throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
AbstractHandlerMethodAdapter#supports 方法源码分析
AbstractHandlerMethodAdapter#supports 没有什么复杂的逻辑,只是判断了 handler 是否是 HandlerMethod 类型,至于 AbstractHandlerMethodAdapter#supportsInternal 方法它默认返回 fasle,也就是说只要 handler 是 HandlerMethod 类型,就算匹配成功。
//org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {//handler 是否是 HandlerMethod 类型 // this.supportsInternal 抽象方法 由子类实现 RequestMappingHandlerAdapter#supportsInternal 默认返回 truereturn handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
}
//org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supportsInternal
protected abstract boolean supportsInternal(HandlerMethod var1);//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#supportsInternal
protected boolean supportsInternal(HandlerMethod handlerMethod) {return true;
}
HandlerExecutionChain#applyPreHandle 方法源码分析
HandlerExecutionChain#applyPreHandle 方法的主要左右就是调用拦截器的 preHandle 方法,如果有某个拦截器的 preHandle 方法返回 false,就会逆向调用返回 true 的拦截器的 triggerAfterCompletion 方法。
//org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {//获取所有拦截器数组HandlerInterceptor[] interceptors = this.getInterceptors();//为空判断if (!ObjectUtils.isEmpty(interceptors)) {//循环调用 拦截器的 preHandle 方法//this.interceptorIndex 记录当前拦截器的位置for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {HandlerInterceptor interceptor = interceptors[i];//判断拦截器的 preHandle 方法返回值if (!interceptor.preHandle(request, response, this.handler)) {//拦截器 preHandle 方法返回 false 则反向调用返回 true 的那些拦截器的 afterCompletion 方法this.triggerAfterCompletion(request, response, (Exception)null);//返回 falsereturn false;}}}return true;
}//org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {//获取所有拦截器HandlerInterceptor[] interceptors = this.getInterceptors();//为空判断if (!ObjectUtils.isEmpty(interceptors)) {//反向遍历 这里是的 interceptorIndex 上面记录了的 --i 就是反向遍历for(int i = this.interceptorIndex; i >= 0; --i) {HandlerInterceptor interceptor = interceptors[i];try {//调用拦截器的 afterCompletion 方法interceptor.afterCompletion(request, response, this.handler, ex);} catch (Throwable var8) {logger.error("HandlerInterceptor.afterCompletion threw exception", var8);}}}}
HandlerExecutionChain#applyPostHandle 方法源码分析
拦截器的 preHandle 方法调用完成后,就会调用 handle 方法处理具体请求(后面分析),handle 方法调用完成后就会调用拦截器的 applyPreHandle 方法, HandlerExecutionChain#applyPreHandle 方法的主要左右就是逆向调用拦截器的 postHandle 方法。
//org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {//获取所有拦截器HandlerInterceptor[] interceptors = this.getInterceptors();//为空判断if (!ObjectUtils.isEmpty(interceptors)) {for(int i = interceptors.length - 1; i >= 0; --i) {//逆向调用拦截器的 postHandle 方法HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}
本篇主要分析了 Spring MVC 工作流程中比较简单的几个关键点,HandlerAdapter 的适配过程、拦截器的前置后置处理等,这些在流程中比较简单的环节就放在一起分析了,希望可以帮助到有需要的朋友。
欢迎提出建议及对错误的地方指出纠正。
相关文章:
Spring MVC 源码分析之 DispatcherServlet#getHandlerAdapter 方法
前言: 前面我们分析了 Spring MVC 的工作流程源码,其核心是 DispatcherServlet#doDispatch 方法,我们前面分析了获取 Handler 的方法 DispatcherServlet#getHandler 方法,本篇我们重点分析一下获取当前请求的适配器 HandlerAdapt…...

假设检验学习笔记
1. 假设检验的基本概念 1.1. 原假设(零假设) 对总体的分布所作的假设用表示,并称为原假设或零假设 在总体分布类型已知的情况下,仅仅涉及总体分布中未知参数的统计假设,称为参数假设 在总体分布类型未知的情况下&#…...
vue3 watch学习
watch的侦听数据源类型 watch的第一个参数为侦听数据源,有4种"数据源": ref(包括计算属性) reactive(响应式对象) getter函数 多个数据源组成的数组。 //ref const xref(0)//单个ref watch(x,(newX)>{console.…...
推荐的Pytest插件
推荐的Pytest插件 Pytest的插件生态系统非常丰富,以下是一些特别推荐的Pytest插件: pytest-sugar 这个插件改进了Pytest的默认输出,添加了进度条,并立即显示失败的测试。它不需要额外配置,只需安装即可享受更漂亮、更…...

C语言 | Leetcode C语言题解之第124题二叉树中的最大路径和
题目: 题解: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int max; int dfs(struct TreeNode* root){if(!root) return 0;int left dfs(root->left…...

Linux综合实践(Ubuntu)
目录 一、配置任务 1.1 配置该服务器的软件源为中科大软件源 1.2 安装相关软件openssh-server和vim 1.3 设置双网卡,网卡1为NAT模式,网卡2为桥接模式(桥接模式下,使用静态ip,该网卡数据跟实验室主机网络设置相似,除…...
C++面试题其二
19. STL中unordered_map和map的区别 unordered_map 和 map 都是C标准库中的关联容器,但它们在实现和性能方面有显著区别: 底层实现:map 是基于红黑树实现的有序关联容器,而 unordered_map 是基于哈希表实现的无序关联容器。元素…...

系统架构设计师【第9章】: 软件可靠性基础知识 (核心总结)
文章目录 9.1 软件可靠性基本概念9.1.1 软件可靠性定义9.1.2 软件可靠性的定量描述9.1.3 可靠性目标9.1.4 可靠性测试的意义9.1.5 广义的可靠性测试与狭义的可靠性测试 9.2 软件可靠性建模9.2.1 影响软件可靠性的因素9.2.2 软件可靠性的建模方法9.2.3 软件的可靠性模…...
x264 参考帧管理原理:i_poc_type 变量
x264 参考帧管理 x264 是一个开源的 H.264 视频编码软件,它提供了许多高级特性,包括对参考帧的高效管理。参考帧管理是视频编码中的一个重要部分,它涉及到如何存储、更新和使用已经编码的帧以提高编码效率。 x264 参考帧管理的一些关键点总结如下: 参考帧的初始化和重排序:…...

高级Web Lab2
高级Web Lab2 12 1 按照“Lab 2 基础学习文档”文档完成实验步骤 实验截图: 2 添加了Web3D场景选择按钮,可以选择目标课程或者学习房间。...

Linux网络-使用Tcp协议进行网络通信并通过网络接口实现远端翻译
文章目录 Tcp协议Tcp协议常见API接口1. int socket(int domain, int type, int protocol);2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);struct sockaddr 3. int listen(int socket, int backlog);4. int accept(int socket, struct socka…...

实时数据传输:Django 与 MQTT 的完美结合
文章目录 准备工作创建 Django 项目与应用设置 MQTT 服务器编写 Django 视图编写前端模板发布 MQTT 消息运行 Django 项目 在当今互联网应用中,实时数据传输已经成为许多项目的核心需求。无论是社交媒体平台、在线游戏、金融交易还是物联网设备,都需要及…...
创建Django项目及应用
1 创建Project 1个Project可以对应多个app django-admin startproject myproject 2 创建App python manage.py startapp app01 INSTALLED_APPS [# ...app01,app02,# ... ] 如果要让这个应用在项目中起作用,需要在项目的 settings.py 文件的 INSTALLED_APPS 配置…...
Flutter课程分享 -(系统课程 基础 -> 进阶 -> 实战 仿京东商城)
前言 在移动应用开发的世界中,Flutter 作为一款由 Google 推出的开源 UI 软件开发工具包,正迅速赢得开发者们的青睐。其跨平台、高性能、丰富的组件库以及易于学习的特性,使得 Flutter 成为许多开发者的不二选择。然而,对于初学者…...

IDEA 中导入脚手架后该如何处理?
MySQL数据库创建啥的,没啥要说的!自行配置即可! 1.pom.xml文件,右键,add Maven Project …………(将其添加为Maven)【下述截图没有add Maven Project 是因为目前已经是Maven了!&…...

thinkphp6 queue队列的maxTries自定义
前景需求:在我们用队列的时候发现maxtries的个数时255次,这个太影响其他队列任务 我目前使用的thinkphp版本是6.1 第一部定义一个新的类 CustomDataBase(我用的mysql数据库存放的队列) 重写__make 和createPlainPayload方法 …...

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(2)
👨💻个人主页:开发者-曼亿点 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 曼亿点 原创 👨💻 收录于专栏:…...
Kotlin 对象
文章目录 对象表达式(匿名对象)对象的声明 对象表达式(匿名对象) 在 Kotlin 中可以使用object {}声明一个匿名的对象,我们无需声明这个对象的类: fun main() {val any object {fun greet() print("…...
力扣 142题 环形链表Ⅱ 记录
题目描述 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内…...

乐观锁 or 悲观锁 你怎么选?
你有没有听过这样一句话:悲观者正确,乐观者成功。那么今天我来分享下什么是乐观锁和悲观锁。 乐观锁和悲观锁有什么区别,它们什么场景会用 乐观锁 乐观锁基于这样的假设:多个事务在同一时间对同一数据对象进行操作的可能性很…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...