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 悲观锁 你怎么选?
你有没有听过这样一句话:悲观者正确,乐观者成功。那么今天我来分享下什么是乐观锁和悲观锁。 乐观锁和悲观锁有什么区别,它们什么场景会用 乐观锁 乐观锁基于这样的假设:多个事务在同一时间对同一数据对象进行操作的可能性很…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
Yii2项目自动向GitLab上报Bug
Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...
【Vue】scoped+组件通信+props校验
【scoped作用及原理】 【作用】 默认写在组件中style的样式会全局生效, 因此很容易造成多个组件之间的样式冲突问题 故而可以给组件加上scoped 属性, 令样式只作用于当前组件的标签 作用:防止不同vue组件样式污染 【原理】 给组件加上scoped 属性后…...
