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

【springboot入门-mvc常用注解使用方式及原理】

常用注解

  • @PathVariable:用于从URL路径中提取变量。
  • @RequestHeader:用于从HTTP请求头中获取数据。
  • @ModelAttribute:用于获取请求参数(包括URL参数和POST请求的表单数据),也可以用于将数据绑定到对象上。
  • @RequestParam:用于获取URL参数。
  • @CookieValue:用于获取请求中的Cookie值。
  • @RequestBody:用于获取请求体中的数据,通常用于处理POST请求中的JSON或XML数据。

java代码示例(普通)

import org.springframework.web.bind.annotation.*;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;@RestController
@RequestMapping("/api")
public class DataController {// 使用 @PathVariable 从URL路径中获取变量@GetMapping("/path/{userId}/profile")public String getPathVariable(@PathVariable String userId) {return "User ID from Path Variable: " + userId;}// 使用 @RequestHeader 从请求头中获取数据@GetMapping("/header")public String getRequestHeader(@RequestHeader("X-Request-ID") String requestId) {return "Request ID from Header: " + requestId;}// 使用 @ModelAttribute 获取请求参数并绑定到对象@PostMapping("/modelAttribute")public String handleModelAttribute(@ModelAttribute User user) {return "User Name: " + user.getName() + ", Age: " + user.getAge();}// 使用 @RequestParam 获取URL参数@GetMapping("/params")public String getRequestParam(@RequestParam("name") String name, @RequestParam("age") int age) {return "Name: " + name + ", Age: " + age;}// 使用 @CookieValue 获取Cookie值@GetMapping("/cookie")public String getCookieValue(@CookieValue("session") String sessionCookie) {return "Session Cookie: " + sessionCookie;}// 使用 @RequestBody 获取请求体中的数据@PostMapping("/body")public String handleRequestBody(@RequestBody User user) {return "User Name from Body: " + user.getName() + ", Age: " + user.getAge();}static class User {private String name;private int age;// Getters and setterspublic String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}
}

测试示例(普通)

  1. @PathVariable 测试路径:
GET /api/path/123/profile
  1. @RequestHeader 测试路径:
GET /api/header
请求头中需要包含 X-Request-ID: 12345。
  1. @ModelAttribute 测试路径:
POST /api/modelAttribute
{"name": "John Doe","age": 30
}
  1. @RequestParam 测试路径:
GET /api/params?name=John&age=30
  1. @CookieValue 测试路径:
GET /api/cookie
Cookie: session=abc123
  1. @RequestBody 测试路径:
POST /api/body
{"name": "Jane Doe","age": 25
}

特殊(传数组)

  1. 使用 @RequestBody 传递JSON数组
    • 如果你使用POST请求并通过请求体传递一个JSON数组,你可以使用@RequestBody注解来接收这个数组。在Spring Boot中,你可以使用HttpEntity或者直接使用@RequestBody来接收数组。
    @RestController
    @RequestMapping("/api")
    public class ListController {// 使用 @RequestBody 接收JSON数组@PostMapping("/list")public String handleRequestBodyList(@RequestBody List<User> users) {return "Received " + users.size() + " users";}
    }
    
    • 测试路径和请求体
    POST /api/list
    [{"name": "John Doe", "age": 30},{"name": "Jane Doe", "age": 25}
    ]
    
  2. 使用 @RequestParam 传递多个相同参数
    • 如果你使用GET请求并通过URL参数传递多个相同名称的参数,你可以使用@RequestParam注解来接收这些参数,并将其转换为列表。
    @RestController
    @RequestMapping("/api")
    public class ListController {// 使用 @RequestParam 接收多个相同参数@GetMapping("/params")public String getRequestParamList(@RequestParam("userId") List<String> userIds) {return "Received user IDs: " + Arrays.toString(userIds.toArray());}
    }
    
    • 测试路径和参数:
    GET /api/params?userId=1&userId=2&userId=3
    
  3. 使用 @ModelAttribute 传递表单数据
    • 如果你使用POST请求并通过表单数据传递一个列表,你可以使用@ModelAttribute注解来接收这个列表。这通常用于处理表单提交。
    @RestController
    @RequestMapping("/api")
    public class ListController {// 使用 @ModelAttribute 接收表单数据@PostMapping("/form")public String handleModelAttribute(@ModelAttribute List<User> users) {return "Received " + users.size() + " users";}
    }
    
    • 测试路径和请求体:
    POST /api/form
    请求体中包含表单数据(使用application/x-www-form-urlencoded):
    userId=1&userId=2&userId=3
    或者,如果你使用JSON格式的请求体,你需要自定义一个包装类来接收这个列表。
    
  4. 使用自定义包装类
    • 对于复杂的数据结构,你可以创建一个自定义的包装类来接收列表。
    public class UserListWrapper {private List<User> users;// Getters and setterspublic List<User> getUsers() {return users;}public void setUsers(List<User> users) {this.users = users;}
    }@RestController
    @RequestMapping("/api")
    public class ListController {// 使用自定义包装类接收JSON数组@PostMapping("/custom")public String handleCustomList(@RequestBody UserListWrapper wrapper) {return "Received " + wrapper.getUsers().size() + " users";}
    }
    
    • 测试路径和请求体:
    POST /api/custom
    {"users": [{"name": "John Doe", "age": 30},{"name": "Jane Doe", "age": 25}]
    }
    

原理分析

主要是DispatcherServlet 的doDispatch方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 获取当前请求的HandlerMappedHandler mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 获取HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 执行前置拦截器if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 调用Handler(包含参数解析器处理参数过程)ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}

在handle方法内部,HandlerAdapter会使用一系列的参数解析器(HandlerMethodArgumentResolver)来处理Controller方法的参数。这些解析器负责从请求中提取数据,并将其转换为Controller方法所需的参数类型。

参数解析的主要代码位于RequestMappingHandlerAdapter的handleInternal方法中

protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);// 如果synchronizeOnSession为true且能从request获取会话对象,则以同步方式执行handlerif (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}} else {mav = invokeHandlerMethod(request, response, handlerMethod);}} else {mav = invokeHandlerMethod(request, response, handlerMethod);}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return null;}applyDefaultViewName(request, mav);MappedHandler mappedHandler = getHandlerExecutionChain(handlerMethod);mappedHandler.applyPostHandle(request, response, mav);return mav;
}

在invokeHandlerMethod方法中,会遍历所有的参数解析器,寻找能够解析当前参数的解析器,并将其缓存。然后,通过参数解析器将需要的参数从请求中获取出来,并进行类型转换和数据绑定。

public Object invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);invocableMethod.invokeAndHandle(request, response);return invocableMethod.getReturnValue();
}

在ServletInvocableHandlerMethod的invokeAndHandle方法中,会调用getMethodArgumentValues方法来获取参数值,这个方法会遍历所有的参数解析器,使用它们来解析参数。

public Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] == null) {HandlerMethodArgumentResolver resolver = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);args[i] = resolver.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}}return args;
}

在这个方法中,resolvers.resolveArgument会调用具体的参数解析器来解析参数。这些解析器主要有:

  • RequestParamMethodArgumentResolver:处理@RequestParam注解的参数。
  • PathVariableMethodArgumentResolver:处理@PathVariable注解的参数。
  • RequestBodyMethodArgumentResolver:处理@RequestBody注解的参数,通常用于接收JSON或XML格式的请求体。
  • RequestHeaderMethodArgumentResolver:处理@RequestHeader注解的参数。
  • CookieValueMethodArgumentResolver:处理@CookieValue注解的参数。
    每个解析器都实现了HandlerMethodArgumentResolver接口,它们负责从请求中提取数据,并将其转换为Controller方法所需的参数类型。

相关文章:

【springboot入门-mvc常用注解使用方式及原理】

常用注解 PathVariable&#xff1a;用于从URL路径中提取变量。RequestHeader&#xff1a;用于从HTTP请求头中获取数据。ModelAttribute&#xff1a;用于获取请求参数&#xff08;包括URL参数和POST请求的表单数据&#xff09;&#xff0c;也可以用于将数据绑定到对象上。Reque…...

滚雪球学Redis[4.2讲]:Redis Sentinel 深度解析:工作原理、配置与高可用架构下的故障转移

全文目录&#xff1a; &#x1f389;前言&#x1f6a6;4.2 Redis Sentinel&#x1f504;Sentinel的工作原理Sentinel的选举机制 ⚙️Sentinel的配置与使用示例&#xff1a;配置Redis SentinelSentinel自动故障转移过程示例 &#x1f9e9;高可用架构下的故障转移常见问题与优化实…...

Vue3 -- 设置分页,切换分页之后选项仍能保留 控制多个表格的选中不会互相影响

在 Vue 3 中实现分页功能&#xff0c;并确保在切换分页时选中的选项能够保留&#xff0c;同时控制多个表格之间的选中状态不互相影响&#xff0c;可以按照以下步骤进行&#xff1a; 1. 数据结构设计 为每个表格维护独立的选中项和分页状态。可以使用一个对象来存储每个表格的…...

如何在 JSON 中编写“anyOf”语句?

在 JSON 中&#xff0c;anyOf 语句通常用于 JSON Schema&#xff08;JSON 模式&#xff09;中&#xff0c;来定义多个可能的模式&#xff0c;表示数据可以匹配多个子模式中的任意一个。这种功能常用于验证 JSON 数据是否符合某一组可能的条件之一。 1、问题背景 问题&#xff…...

python开发环境配置

下载python安装包安装python配置环境变量调整类库下载位置 安装python 安装python是指安装python的基础编译环境及python运行所需的必须资源&#xff0c;类似于安装java的JDK python2与python3差异 进行python安装前&#xff0c;需要先了解python2和python3的差异&#xff0…...

QT开发--QT SQL模块

第十五章 QT SQL模块 15.1 QT SQL模块概览 Qt SQL模块是Qt框架中操作数据库的组件&#xff0c;提供易用API&#xff0c;支持SQLite、MySQL等多种数据库。它包含数据库驱动与连接功能。 15.1.1 QSqlDatabase 类 在Qt SQL模块中&#xff0c;数据库驱动基于QSqlDriver类&#xf…...

如何保证接口幂等性?

一、什么是接口幂等性&#xff1f; 幂等性是指&#xff1a;同一请求&#xff0c;执行很多次&#xff0c;最终结果都一样。 二、为什么会产生接口幂等性问题&#xff1f; 那么&#xff0c;什么情况下&#xff0c;会产生接口幂等性的问题呢&#xff1f; 网络波动, 可能会引起重…...

【9718】基于springboot+vue的生鲜交易系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 生鲜交易管理方面的任务繁琐,以至于交易市场每年都在生…...

Spring循环依赖解决方案

解决方案 使用提前暴露机制三级缓存进行解决 singletonObjects一级缓存&#xff0c;存放完整的 Bean。earlySingletonObjects二级缓存&#xff0c;存放提前暴露的Bean&#xff0c;Bean 是不完整的&#xff0c;未完成属性注入和执行 init 方法。singletonFactories三级缓存(用…...

解决 IntelliJ IDEA 运行时 “Command line is too long“ 问题

文章目录 文章标题&#xff1a;解决 IntelliJ IDEA 运行时 "Command line is too long" 问题简介问题描述解决方案代码示例代码示例1&#xff1a;使用JAR Manifest代码示例2&#xff1a;使用Classpath File代码示例3&#xff1a;优化项目依赖 结论进一步的资源 文章标…...

鸿蒙网络编程系列5-TCP连接超时分析

1. TCP连接超时简介 TCP是面向连接的协议&#xff0c;通过三次握手建立连接&#xff0c;但是&#xff0c;在建立连接的过程中对方有可能没有响应&#xff0c;这时候发起连接的一方会重试&#xff0c;如果重试多次仍然没有响应&#xff0c;就会触发超时&#xff0c;从而导致连接…...

金蝶云星空移动字段后关闭页面后重新打开无效

有同事反馈&#xff0c;单据的明细字段里面移动了字段&#xff0c;然后退出&#xff0c;其他字段都能按最后排版的位置显示&#xff0c;有个别字段始终无法按照排版的位置显示。 只需要打开BOS平台&#xff0c;找到对应字段&#xff0c;然后更改可见性。...

幂律分布笔记

一、幂律分布的数据拟合 数据分箱&#xff1a; 所谓分箱就是对原始数据进行分组&#xff0c;然后对每一组内的数据进行平滑处理。常见的分箱方式主要有等深分箱、等宽分箱、用户自定义等 对数分箱&#xff1a; 对原数据进行分箱&#xff0c;第i个箱的宽度为bi&#xff0c;b…...

一些NLP代表性模型

&#xff08;一&#xff09;BERT 由Bidirectional Encoder Representations from Transformers的首字母组成&#xff0c;是encoder-only结构类型的代表。 模型分预训练和微调两步&#xff0c;预训练任务有两类&#xff1a;masked language model(MLM)、next sentence predict…...

低代码移动端开发:未来的趋势与挑战

什么是低代码移动端开发&#xff1f; 低代码移动端开发平台允许开发者通过可视化界面和少量编码来构建应用程序。相较于传统的代码开发&#xff0c;低代码平台大大降低了技术和学习门槛&#xff0c;使非专业开发人员也能参与到移动应用的开发过程中。 低代码移动端开发的优势 …...

【Linux】嵌入式Linux系统的组成、u-boot编译

Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下&#xff1a;1.2 PC机—Windows系统启动流程&#xff08;PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似&#xff09; 二、编译u-boot2.1 u-bo…...

Qt打开excel文件,并读取指定单元格数据

1. 下载并安装QXlsx库&#xff0c;详见之前的博文Qt子线程创建excel文件报错QObject: Cannot create children for a parent that is in a different thread.-CSDN博客 2. // 创建一个XlsxDocument对象QString filename "D:\\mydocuments\\data_acquisition\\data\\tes…...

适合下班回家做的小副业,用AI做视频,几天时间3000+

大家好&#xff0c;今天要给大家分享的项目是定制儿歌&#xff0c;精准定位宝妈群体&#xff0c;每天轻松赚500&#xff01; ***01* 项目原理 父母都非常疼爱自己的孩子&#xff0c;愿意为孩子提供独特的东西。而我们正是利用这一点&#xff0c;通过免费AI工具生成专属的儿童…...

git的基本操作 + 分支管理

一、基本操作 1. 修改文件 Git比其他的版本管理器设计得更加优秀&#xff0c;因为Git追踪并管理的是修改&#xff0c;而非文件。 修改一个文件&#xff0c;不管你是添加一行&#xff0c;或者删除一行&#xff0c;还是添加了又删除了&#xff0c;甚至你创建了一个新文件&…...

VRRP

1、VRRP简介 虚拟路由冗余协议 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;通过把几台路由设备联合组成一台虚拟的路由设备&#xff0c;将虚拟路由设备的IP地址作为用户的默认网关实现与外部网络通信。当网关设备发生故障时&#xff08;单点故障&#xf…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...