当前位置: 首页 > news >正文

源码解析SpringMVC之RequestMapping注解原理

1、启动初始化

核心:得到应用上下文中存在的全部bean后依次遍历,分析每一个目标handler & 目标方法存在的注解@RequestMapping,将其相关属性封装为实例RequestMappingInfo。最终将 uri & handler 之间的映射关系维护在类AbstractHandlerMethodMapping中的内部类RequestMappingInfo中。

利用RequestMappingHandlerMappingInitializingBean接口特性来完成请求 uri & handler 之间的映射关系。具体详情参考其父类AbstractHandlerMethodMapping实现其功能,如下:

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {//内部类private final MappingRegistry mappingRegistry = new MappingRegistry();protected void initHandlerMethods() {for (String beanName : getCandidateBeanNames()) {//从应用上下文中获取全部的beanprocessCandidateBean(beanName);}}protected void processCandidateBean(String beanName) {Class<?> beanType = obtainApplicationContext().getType(beanName);// 判断是否为Handler的条件为:是否存在注解Controller 或者 RequestMappingif (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}protected void detectHandlerMethods(Object handler) {//handler为String类型的beanNameClass<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {Class<?> userType = ClassUtils.getUserClass(handlerType);// 集合methods其key:反射中Method类。value:RequestMappingInfoMap<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {// 调用具体的实现类,例如RequestMappingHandlerMappingreturn getMappingForMethod(method, userType);});methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}}protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method);}
}

1.1、RequestMappingHandlerMapping

解析目标类 & 目标方法之@RequestMapping注解相关属性。

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping{@Override@Nullableprotected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {//将目标方法存在的@RequestMapping注解相关属性封装为RequestMappingInfoRequestMappingInfo info = createRequestMappingInfo(method);if (info != null) {//如果目标handler也存在@RequestMapping注解,则也封装为RequestMappingInfoRequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);if (typeInfo != null) {// 将目标方法@RequestMapping相关属性与目标handler之@RequestMapping相关属性合并起来info = typeInfo.combine(info);}String prefix = getPathPrefix(handlerType);//获取目标handler之url前缀if (prefix != null) {info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);}}return info;}private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);return (requestMapping != null ? createRequestMappingInfo(requestMapping, null) : null);}protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping,RequestCondition condition) {// 获取某个具体目标方法其@RequestMapping注解相关属性,最终封装为RequestMappingInfoRequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());return builder.options(this.config).build();}
}

1.2.MappingRegistry

public abstract class AbstractHandlerMethodMapping<T> {class MappingRegistry {private final Map<T, MappingRegistration<T>> registry = new HashMap<>();//集合mappingLookup之key:RequestMappingInfo。value:HandlerMethodprivate final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();public void register(T mapping, Object handler, Method method) {...// 目标类的目标方法最终封装为HandlerMethod  handler实为目标bean的beanNameHandlerMethod handlerMethod = createHandlerMethod(handler, method);this.mappingLookup.put(mapping, handlerMethod);List<String> directUrls = getDirectUrls(mapping);for (String url : directUrls) {this.urlLookup.add(url, mapping);}...this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));}}
}

2、处理请求

SpringMVC利用请求url获取目标handler。
在这里插入图片描述

public class DispatcherServlet extends FrameworkServlet {@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {// 围绕 RequestMappingHandlerMapping 展开HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
}
public abstract class AbstractHandlerMethodMapping<T>  implements InitializingBean {private final MappingRegistry mappingRegistry = new MappingRegistry();@Overrideprotected HandlerMethod getHandlerInternal(HttpServletRequest request){// 获取requestUriString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);// 从 mappingRegistry 获取目标handlerHandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) {List<Match> matches = new ArrayList<>();// 利用 lookupPath 从MappingRegistry相关属性中获取目标handler之TList<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);// 通过章节1得知,返回的类为 RequestMappingInfo,即@RequestMapping注解的相关属性if (directPathMatches != null) {//分析请求addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {// No choice but to go through all mappings...addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}// 如果局部matches中元素为空,说明请求头校验失败if (!matches.isEmpty()) {Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));matches.sort(comparator);Match bestMatch = matches.get(0);...request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);handleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {//最终此处抛出相关的异常return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}}
}

请求头相关参数校验失败抛出的异常包括:HttpRequestMethodNotSupportedExceptionHttpMediaTypeNotSupportedExceptionHttpMediaTypeNotAcceptableExceptionUnsatisfiedServletRequestParameterException

但是这些类型的异常好像不能被全局拦截器拦截处理。

2.1.解析请求头相关属性

核心就是校验请求request中相关属性跟RequestMappingInfo中属性是否匹配。

public abstract class AbstractHandlerMethodMapping<T>  implements InitializingBean {private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {for (T mapping : mappings) {T match = getMatchingMapping(mapping, request);if (match != null) {//正常请求校验都通过,最终返回重新包装后的RequestMappingInfomatches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));}}}
}
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping {@Overrideprotected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {return info.getMatchingCondition(request);}
}

2.1.1、RequestMappingInfo

该类中的相关属性是对 注解@RequestMapping之字段属性的封装。

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {//请求方式: 校验Method属性,即是否为post or get 等相对应的请求方式RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);if (methods == null) {return null;}//请求参数ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);if (params == null) {return null;}//consumer参数:请求头中contentType是否一致。HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);if (headers == null) {return null;}//producer参数:请求头中Accept是否一致ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);if (consumes == null) {return null;}ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);if (produces == null) {return null;}PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);if (patterns == null) {return null;}RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);if (custom == null) {return null;}return new RequestMappingInfo(this.name, patterns,methods, params, headers, consumes, produces, custom.getCondition());}
}

相关文章:

源码解析SpringMVC之RequestMapping注解原理

1、启动初始化 核心&#xff1a;得到应用上下文中存在的全部bean后依次遍历&#xff0c;分析每一个目标handler & 目标方法存在的注解RequestMapping&#xff0c;将其相关属性封装为实例RequestMappingInfo。最终将 uri & handler 之间的映射关系维护在类AbstractHand…...

biocParallel学习

我好像做了一个愚蠢的测试 rm(listls()) suppressPackageStartupMessages({library(SingleCellExperiment)library(scMerge)library(scater)library(Matrix) })setwd("/Users/yxk/Desktop/test/R_parallel/") load("./data/exprsMat.RData") load(".…...

AWTK实现汽车仪表Cluster/DashBoard嵌入式GUI开发(六):一个AWTK工程

一个AWTK工程基于C/C++编写,可以分为如下几步: 结合下图,看懂启动的部分。一般一个AWTK工程,需要实现哪些部分,就是其中开始之后白色的部分,比如调用main函数和gui_app_start时会做一些操作,比如asset_init和application_init时要做一些设置,还有退出的函数application…...

MySQL主从复制(基于binlog日志方式)

目录 一、什么是主从复制&#xff1f;二、主从复制原理、存在问题和解决方法2.1.主从复制原理2.2.主从复制存在的问题以及解决办法2.3.主从复制的同步模型2.4.拓展—Mysql并行复制 三、主从复制之基于binlog日志方式3.1.bin-log日志简介3.2.bin-log的使用3.2.1.开启binlog3.2.2…...

计算机网络【CN】介质访问控制

信道划分介质访问控制 FDMTDMWDMCDM【掌握eg即可】 随机介质访问控制 CSMA 1-坚持CSMA 非坚持CSMA p-坚持CSMA 空闲时 立即发送数据 立即发送数据 以概率P发送数据&#xff0c;以概率1-p推迟到下一个时隙 忙碌时 继续坚持侦听 放弃侦听&#xff0c;等待一个随机的时…...

CDR和AI哪个软件更好用?

设计软件市场中&#xff0c;CorelDRAW和Adobe Illustrator&#xff08;简称AI&#xff09;无疑是两大重量级选手。它们各自拥有庞大的用户群和丰富的功能&#xff0c;但究竟哪一个更好用&#xff1f;本文将从多个角度出发&#xff0c;对这两款软件进行全面而深入的比较&#xf…...

保姆级认识AVL树【C++】(精讲:AVL Insert)

目录 前言 一&#xff0c;概念 二&#xff0c;定义 三&#xff0c;insert 1. 插入情况 情况一&#xff1a; 情况二&#xff1a; 情况三&#xff1a; 2. 旋转方法 法一&#xff1a;左单旋法 法二&#xff1a;右单旋法 法三&#xff1a;先左后右双旋法 法四&#xf…...

pinia中使用reactive声明变量,子页面使用时,值未改变,即不是响应式的(解决方法)

reactive赋值无效&#xff01;reactive 不要直接data赋值&#xff01;&#xff01;&#xff01;会丢失响应式的&#xff0c;只能通过obj.属性 属性值赋值 方法一. pinia中直接使用ref定义变量即可 export const useUserStoredefineStore(user,()>{let loginUserreactive({…...

基于springboot零食商城管理系统

功能如图所示 摘要 这基于Spring Boot的零食商城管理系统提供了强大的购物车和订单管理功能。用户可以在系统中浏览零食产品&#xff0c;并将它们添加到购物车中。购物车可以保存用户的选购商品&#xff0c;允许随时查看已选择的商品和它们的数量。一旦用户满意&#xff0c;他们…...

C++程序练习

定义一个类CheckPath&#xff0c;它由两个public方法组成&#xff1a; 1&#xff09; checkPath&#xff1a;检查传入的字符串指定的路径是否存在&#xff0c;存在返回true&#xff0c;否则返回false。 2&#xff09; createFilePath&#xff1a;根据传入的字符串指定的路径&…...

Golang 继承

在面向对象的编程语言中&#xff0c;继承是一种重要的机制&#xff0c;它允许子类继承父类的属性和方法。然而&#xff0c;Go语言在设计时没有直接支持传统意义上的继承&#xff0c;而是提供了一种更为灵活和简洁的方式来实现类似的功能。本文将探讨Golang中实现继承的方法和最…...

棋盘格测距-单目相机(OpenCV/C++)

一、文章内容简述&#xff1a; 1’ 通过cv::findChessboardCorners寻找棋盘格角点 2‘ 用cv::solvePnP计算旋转向量rvec和平移向量tvec 3’ 通过公式计算相机到棋盘格的距离 float distance sqrt(tvec.at<double>(0,0) * tvec.at<double>(0,0) tvec.at<do…...

031-从零搭建微服务-监控中心(一)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;mingyue: &#x1f389; 基于 Spring Boot、Spring Cloud & Alibaba 的分布式微服务架构基础服务中心 源…...

vue中使用xlsx插件导出多sheet excel实现方法

安装xlsx&#xff0c;一定要注意版本&#xff1a; npm i xlsx0.17.0 -S package.json&#xff1a; {"name": "hello-world","version": "0.1.0","private": true,"scripts": {"serve": "vue-c…...

Linux - 进程的优先级 和 如何使用优先级调度进程

理解linux 当中如何做到 把一个PCB 放到多个 数据结构当中 在Linux 当中&#xff0c;一个进程的 PCB 不会仅仅值存在一个 数据结构当中&#xff0c;他既可以在 某一个队列当中&#xff0c;又可以在 一个 多叉树当中。 队列比如 cpu 的 运行队列&#xff0c;键盘的阻塞队列等等…...

支持控件drag和click

在 MouseDown 事件触发 DoDragDrop 拖拽操作时&#xff0c;Click 事件通常无效&#xff0c;因为 DoDragDrop 方法会捕获鼠标事件并等待拖拽操作完成。 有一个简单地思路解决这个问题 当MouseDow时&#xff0c;触发定时器&#xff0c;延迟100s定时器到时后&#xff0c;进入dra…...

AIR101 LuatOS LVGL 显示多个标签例程

屏幕资料 AIR101与屏幕连接 PC端仿真环境合宙官方PC端版本环境搭建教程 PC电脑仿真 -- sys库是标配 _G.sys require("sys") sys.taskInit(function()local cnt0lvgl.init(480,320)--lvgl初始化local cont lvgl.cont_create(nil, nil);-- lvgl.cont_set_fit(cont, …...

Istio实战(七)- Bookinfo 部署

1. Istio Bookinfo示例 1.1 部署Bookinfo # kubectl apply -f /apps/istio/samples/bookinfo/platform/kube/bookinfo.yaml -n hr1.2 确认Bookinfo已经部署正常 先确认以下pod和service已经被正确创建 # kubectl get pods -n hr NAME READY …...

出差学小白知识No5:|Ubuntu上关联GitLab账号并下载项目(ssh key配置)

1 注冊自己的gitlab账户 有手就行 2 ubuntu安装git &#xff0c;并查看版本 sudo apt-get install git git --version 3 vim ~/.ssh/config Host gitlab.example.com User your_username Port 22 IdentityFile ~/.ssh/id_rsa PreferredAuthentications publickey 替换gitl…...

FL Studio21.2中文版多少钱?值得下载吗

水果&#xff0c;全称Fruity Loop Studio&#xff0c;简称FL Studio。是一款全能的音乐制作软件&#xff0c;经过二十多年的演化更迭&#xff0c;其各项功能非常的先进。其开创性的Pat\song模式&#xff0c;也为初学者的学习提供了便利。那么水果音乐制作软件需要多少钱呢&…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...