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

news/2024/10/18 5:23:00/

常用注解

  • @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方法所需的参数类型。

http://www.ppmy.cn/news/1539906.html

相关文章

Qt C++ 编程中定义了一个槽函数(slot)deleteLater的作用

这行代码是在 Qt C编程中定义了一个槽函数&#xff08;slot&#xff09;deleteLater。 在 Qt 框架中&#xff0c;Q_SLOTS关键字用于声明类中的槽函数。deleteLater是一个非常有用的函数&#xff0c;它会安排接收对象在事件循环返回后被删除。 通常在以下情况下会使用deleteLa…

作为一名测试工程师如何学习Kubernetes(k8s)技能

前言 Kubernetes(K8s)作为云原生时代的关键技术之一&#xff0c;对于运维工程师、开发工程师以及测试工程师来说&#xff0c;都是一门需要掌握的重要技术。作为一名软件测试工程师&#xff0c;学习Kubernetes是一个有助于提升自动化测试、容器化测试以及云原生应用测试能力的重…

kubernetes(k8s)面试之2024

1、什么是k8s&#xff1f; K8s是kubernetes的简称&#xff0c;其本质是一个开源的容器编排系统&#xff0c;主要用于管理容器化的应用&#xff0c; 简单点就是k8s是一个编排容器的系统&#xff0c;一个可以管理容器应用全生命周期的工具&#xff0c;从创建应用&#xff0c;应用…

K8s高级调度--CronJob与污点容忍及亲和力

文章目录 CronJobCronJob 的核心概念Job调度时间表并发策略启动历史保留 CronJob YAML 配置示例Cron 表达式 CronJob 实际应用场景定期数据备份日志清理任务 污点和容忍污点的概念污点的三种效应污点和容忍的工作流程设置污点和容忍1. 给节点添加污点2. 给 Pod 添加容忍 实际应…

植物大战僵尸杂交版即将新增内容介绍

新BOSS僵尸&#xff1a;埃德加二世 特点&#xff1a;埃德加博士的克隆体&#xff0c;驾驶小型机甲。体型&#xff1a;小于原版僵王的头。血量&#xff1a;120000&#xff0c;是原版僵王复仇的2倍。免疫效果&#xff1a;减速、冰冻、黄油效果&#xff0c;能阻挡子弹。行为模式&…

vue3中监视 Reactive对象中的属性

watch 的第一个参数可以是不同形式的“数据源”&#xff1a;它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组 一、框架&#xff1a; <template><div class"divBox"><h2>姓名&#xff1a;{{ person.…

写一个程序拷贝文件 C语言版(含源码)

在当前目录下放一个文件data.txt&#xff0c;写一个程序&#xff0c;将data.txt文件拷贝一份&#xff0c;生成data_copy.txt文件。 基本思路&#xff1a; 打开文件data.txt&#xff0c;读取数据打开文件data_copy.txt&#xff0c;写数据从data.txt中读取数据存放到data_copy.…

什么是NLP?

文章目录 NLP概述NLP三个字母含义NLP发展历程NLP杰出贡献者NLP应用前景NLP对人生的积极影响NLP基本精神&#xff1a;12条前提假设NLP部分基本术语结束语 NLP概述 大家好&#xff0c;今天我们将一起探索NLP&#xff0c;即神经语言程序学这一强大的领域。NLP是对人类主观经验的研…