SpringBoot请求映射源码分析(没看过源码的小白也能懂,比针尖还细)

简介: SpringBoot请求映射源码分析(没看过源码的小白也能懂,比针尖还细)

一.前言

1.Rest风格的请求

我们现在一般喜欢用Rest风格的请求,即使用HTTP请求方式动词来表示对资源的操作。
举个例子:

  • 比如我们以前对学生信息进行相关的增删改查操作,定义的url路径可能是这样的:
    • /getStudent 获取学生信息
    • /deleteStudent 删除学生信息
    • /editStudent 修改学生信息
    • /savaStudent 保存学生信息
      如果这个项目比较大,我们的路径起名字都感觉很麻烦。
  • 现在我们用Rest风格这样做:
    • 所有对学生的操作都叫/Student,如何表示对学生的增删改查呢,使用HTTP请求方式动词来表示对资源的操作。
    • GET 方式请求表示获取学生信息
    • DELETE 方式请求表示删除学生信息
    • PUT 方式请求表示修改学生信息
    • POST 方式请求表示保存学生信息
      利用这些不同的请求方式动词来区分不同的请求。

2.表单如何发出delete和put请求

我们以前用SpringMVC来完成这些事情,我们需要配置一个叫HiddenHttpMethodFilter的Filter;
我们来到WebMvcAutoConfiguration中,可以看到它已经配置了一个HiddenHttpMethodFilter,如下:
在这里插入图片描述

也就是说默认我们的rest功能是可以用的,但是它通过@ConditionalOnProperty设置配置属性前缀spring.mvc.hiddenmethod.filter的配置属性名字enabled默认为false,来默认该功能是不开启的,我们在配置文件中手动开启它:

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true #选择性开启

为什么要配置HiddenHttpMethodFilter呢,因为我们的表单提交的请求方式中只支持GETPOST方式的请求,它不支持DELETEPUT 方式的请求,如下:

我们的表单的method只有两个选项,get和post:
在这里插入图片描述
那我们如何用表单发出delete和put请求呢? 我们可以这样做(为什么这样做我们之后在源码分析中会讲到),method还是等于post请求方式,但是添加了一个name="_method"value="DELETE"或者value="put"的input标签(value中的delete或者put大小写都可以,之后我们在讲解源码的过程中会看到SpringBoot会自动把其值全部变成大写),并且我们把这个input标签隐藏起来,如下:
在这里插入图片描述
这里先说一下为什么name="_method",这已经在HiddenHttpMethodFilter类中写好了,如下:
在这里插入图片描述
我们也可以调用setMethodParam设置它的值,如下:
在这里插入图片描述
自己新建一个配置类,用@Bean注入对象到容器中,在该方法返回对象前,调用setMethodParam来设置methodParam(也就是name)的值,如下:(这里只是做一个演示,下文的所有代码中没有该配置类)

在这里插入图片描述

3.完整代码示例:

Controller如下:

@RestController
public class HelloController {
     
    
//    @RequestMapping(value = "/student",method = RequestMethod.GET)
    @GetMapping("/student")
    public String getUser(){
     
        return "`GET` 方式请求表示获取学生信息";
    }

//    @RequestMapping(path = "/student",method = RequestMethod.POST)
    @PostMapping("/student")
    public String saveUser(){
     
        return "`POST` 方式请求表示保存学生信息";
    }

//    @RequestMapping(value = "/student",method = RequestMethod.PUT)
    @PutMapping("/student")
    public String putUser(){
     
        return "`PUT` 方式请求表示修改学生信息";
    }

//    @RequestMapping(value = "/student",method = RequestMethod.DELETE)
    @DeleteMapping("/student")
    public String deleteUser(){
     
        return "`DELETE` 方式请求表示删除学生信息";
    }
}

index.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>SpringBoot Web ,欢迎您!</h1>
测试REST风格:
<!--method为空默认为get方式请求-->
<form action="/student" method="">
    <input value="GET请求" type="submit">
</form>
<form action="/student" method="post">
    <input value="POST请求"  type="submit">
</form>
<form action="/student" method="post">
    <input name="_method" value="DELETE" type="hidden">
    <input value="DELETE请求" type="submit">
</form>
<form action="/student" method="post">
    <input name="_method" value="put" type="hidden">
    <input value="PUT请求" type="submit">
</form>
<hr>
</body>
</html>

欢迎页面:
在这里插入图片描述

点击测试get请求:
在这里插入图片描述

点击测试post请求:
在这里插入图片描述

点击测试delete请求:
在这里插入图片描述

点击测试put请求:
在这里插入图片描述
可以看到这四种方式的请求我们都可以获取并访问到,那么底层源码是怎么写的呢,接下来我将具体讲解源码所做的一些事情。

二.源码分析

1.HiddenHttpMethodFilter类中的doFilterInternal方法

上述提到的hiddenHttpMethodFilter()方法中返回了OrderedHiddenHttpMethodFilter类的对象,把OrderedHiddenHttpMethodFilter对象注册到容器中:
在这里插入图片描述
而OrderedHiddenHttpMethodFilter类有继承了HiddenHttpMethodFilter类:
在这里插入图片描述
现在我们只需要来看HiddenHttpMethodFilter类了,而HiddenHttpMethodFilter类中的doFilterInternal方法是处理上述过程的主要方法体,我们给doFilterInternal方法打上断点,如下:
在这里插入图片描述

2.一步步分析源码:

启动debug来调试程序,接下来我们来一步步分析:
首先我们访问我们的欢迎页面:在这里插入图片描述
点击put请求,表单提交会带上name = value的信息, 在我们的put表单中,name="_method" value=“put”,如下:
在这里插入图片描述

所以表单提交时会带上_method=put的信息。

1.第一行代码

表单提交后请求会被HiddenHttpMethodFilter拦截,来到了doFilterInternal方法中,首先拿到我们的原生请求request,把它赋值给一个局部变量requestToUse,如下:
第一行代码:
在这里插入图片描述

2.第二行代码

然后进入if条件判断中,判断我们的原生request请求方式是不是POST方式(也就是我们form表单的method属性是不是post方式)和判断当前请求是否没有错误,如下:

刚才我们写的form表单:

在这里插入图片描述
第二行代码:
在这里插入图片描述
因为我们点击的是put,它的method属性是post,然后也没有什么错误,我们满足条件,进入if语句体中。

3.第三行代码

然后呢,接下来它调用request.getParameter方法获取请求参数并把它赋值给局部变量paramValue:
第三行代码:
在这里插入图片描述
其中request.getParameter实参的this.methodParam是什么呢?我们点进去看一下,如下:

在这里插入图片描述

它就是_method字符,这也就解释了为什么之前我们要在form表单中再添加一个input标签,其name值为_method,request.getParameter(_method)就或许到了其value值,因为我们刚才点击put请求,表单提交会带上name = value的信息, 在我们的put表单中,name="_method" value=“put”,所以局部变量paramValue得到的值为value的值,为put。

4.第四行代码

接着往下走,我们来到第四行代码,这里又是一个if语句判断,StringUtils.hasLength(paramValue)方法它判断的是字符串是否为null或者为为空,显然我们的paramValue不为null也不为空,为put,满足if判断条件。
StringUtils.hasLength方法:
在这里插入图片描述
第四行代码:
在这里插入图片描述

5.第五行代码

接着往下走,我们的第五行代码是把我们的paramValue局部变量变成大写,也就是把put变成大写的PUT,然后赋值给新的局部变量method,如下:
第五行代码:
在这里插入图片描述

6.第六行代码

又到了if语句判断的地方,这里判断我们的局部变量method(也就是PUT)是否包含在ALLOWED_METHODS这个List集合中,为什么ALLOWED_METHODS是一个集合呢?我们点进去看一下就知道了:
在这里插入图片描述
它是这么一个集合,它里面有PUT、DELETE、和PATCH,它们都是枚举类型,我们分别点进去看一下:
在这里插入图片描述
它们的name方法是得到其名称,也就是同名的字符串,也就得到了PUT,DELETE和PATCH字符串。
我们的局部变量method(也就是PUT)在其中,if语句为true。
第六行代码:
在这里插入图片描述

7.第七行代码

继续往下走,接下来遇到了一个new HttpMethodRequestWrapper新建了一个对象来取代原来的原生request,我们点进这个HttpMethodRequestWrapper中看一下:
在这里插入图片描述
我们发现这个HttpMethodRequestWrapper继承了HttpServletRequestWrapper,我们点进HttpServletRequestWrapper看一下:
在这里插入图片描述
发现这个HttpServletRequestWrapper最终还是实现了HttpServletRequest,所以呢HttpServletRequestWrapper的子类HttpMethodRequestWrapper他还是一个HttpServletRequest请求。
现在我们回到HttpMethodRequestWrapper类中,具体看其类内部是怎么回事:
我们调用了HttpMethodRequestWrapper的构造器,传递了两个参数分别是原生的request请求和method(也就是PUT)请求方式,在构造器中我们把请求方式method赋值给类的属性method,如下:
在这里插入图片描述
然后重写了父类的getMethod方法,返回我们刚刚赋值的method(也就是PUT )。
调用了构造器之后把其创建的对象赋值给了局部变量requestToUse,当我们调用requestToUse的getMethod方法时,我们返回的就是PUT了,这个地方使用了一个包装设计模式,很巧妙。
第七行代码:
在这里插入图片描述

8.第八行代码

调用filter过滤器filterchain的dofilter方法,将把自身接收到的请求request对象和response对象和自身对象即filterchain作为下一个过滤器的dofilter的形参传递过去,这样才能使得过滤器传递下去。但是这里的request对象是requestToUse,也就是我们刚刚重写了getMethod方法的requestToUse,当调用getMethod的方法时返回的是PUT。
第八行代码:
在这里插入图片描述

dubug放行后运行结果:
在这里插入图片描述
上述我们都说的是表单提交的方式发送请求。
如果是客户端直接发送请求,我们的请求方式可以直接是PUT、DELETE等方式。如果不是POST请求它会只运行第一行代码和第八行代码。

目录
相关文章
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
821 7
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
1213 1
|
8月前
|
XML JSON Java
【SpringBoot(三)】从请求到响应再到视图解析与模板引擎,本文带你领悟SpringBoot请求接收全流程!
Springboot专栏第三章,从请求的接收到视图解析,再到thymeleaf模板引擎的使用! 本文带你领悟SpringBoot请求接收到渲染的使用全流程!
591 3
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
731 70
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
920 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
848 0
|
小程序 Java 关系型数据库
weixin117新闻资讯系统设计+springboot(文档+源码)_kaic
本文介绍了一款基于微信小程序的新闻资讯系统,涵盖其开发全过程。该系统采用Java的SSM框架进行后台管理开发,使用MySQL作为本地数据库,并借助微信开发者工具确保稳定性。管理员可通过个人中心、用户管理等功能模块实现高效管理,而用户则能注册登录并查看新闻与视频内容。系统设计注重可行性分析(技术、经济、操作),强调安全性与数据完整性,界面简洁易用,功能全面,极大提升了信息管理效率及用户体验。关键词包括基于微信小程序的新闻资讯系统、SSM框架和MYSQL数据库。
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
1158 5
|
8月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。