【SpringBoot】16 核心功能 - Web开发原理 - 请求参数 - 源码分析

文章目录


一、普通参数与基本注解

在SpringBoot的Web开发中,处理请求参数是最基础且常见的操作。SpringMVC提供了丰富的注解和机制来帮助我们方便地获取请求中的各类参数。

1.1 常用注解

  • @PathVariable:用于获取URL路径中的变量值。
  • @RequestHeader:用于获取HTTP请求头信息。
  • @ModelAttribute:用于将请求参数绑定到模型对象。
  • @RequestParam:用于获取HTTP请求参数。
  • @MatrixVariable:用于获取URL中的矩阵变量。
  • @CookieValue:用于获取Cookie值。
  • @RequestBody:用于获取HTTP请求体内容,通常用于接收JSON或XML数据。

以下是一个使用多种注解的控制器方法示例:

java 复制代码
@RestController
public class ParameterTestController {

    @GetMapping("/car/{id}/owner/{username}")
    public Map<String, Object> getCar(@PathVariable("id") Integer id,
                                      @PathVariable("username") String name,
                                      @PathVariable Map<String, String> pv,
                                      @RequestHeader("User-Agent") String userAgent,
                                      @RequestHeader Map<String, String> header,
                                      @RequestParam("age") Integer age,
                                      @RequestParam("interests") List<String> interests,
                                      @RequestParam Map<String, String> params,
                                      @CookieValue("_ga") String _ga,
                                      @CookieValue("_ga") Cookie cookie) {

        Map<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("name", name);
        map.put("pv", pv);
        map.put("userAgent", userAgent);
        map.put("header", header);
        map.put("age", age);
        map.put("interests", interests);
        map.put("params", params);
        map.put("_ga", _ga);
        map.put("cookie", cookie);

        return map;
    }
}

1.2 Servlet API参数

SpringMVC支持在控制器方法中直接使用Servlet API中的对象作为参数,例如:

  • WebRequest
  • ServletRequest
  • MultipartRequest
  • HttpSession
  • javax.servlet.http.PushBuilder
  • Principal
  • InputStream
  • Reader
  • HttpMethod
  • Locale
  • TimeZone
  • ZoneId

1.3 复杂参数

除了基本参数类型,SpringMVC还支持一些复杂参数类型:

  • Map
  • Model(Map和Model中的数据会被放在request的请求域中,相当于调用request.setAttribute)
  • Errors/BindingResult
  • RedirectAttributes(用于重定向时携带数据)
  • ServletResponse(response对象)
  • SessionStatus
  • UriComponentsBuilder
  • ServletUriComponentsBuilder

1.4 自定义对象参数

SpringMVC支持将请求参数自动绑定到自定义对象,包括自动类型转换与格式化,以及级联封装(即对象中包含其他对象的情况)。

二、请求参数处理源码分析

SpringMVC通过HandlerAdapter组件来处理控制器方法的参数解析和调用。在SpringBoot中,默认配置了多个HandlerAdapter:

  • RequestMappingHandlerAdapter:支持方法上标注@RequestMapping注解的处理器
  • HandlerFunctionAdapter:支持函数式编程的处理器
  • HttpRequestHandlerAdapter:支持HttpRequestHandler接口的处理器
  • SimpleControllerHandlerAdapter:支持Controller接口的处理器

其中,RequestMappingHandlerAdapter是最常用的,它负责解析控制器方法中的各种参数注解。

参数解析过程

当请求到达DispatcherServlet时,它会找到匹配的处理器,然后通过合适的HandlerAdapter来调用处理器方法。参数解析的核心是HandlerMethodArgumentResolver接口,SpringMVC提供了多个实现类来处理不同类型的参数:

  • PathVariableMethodArgumentResolver:处理@PathVariable注解
  • RequestHeaderMethodArgumentResolver:处理@RequestHeader注解
  • RequestParamMethodArgumentResolver:处理@RequestParam注解
  • ServletCookieValueMethodArgumentResolver:处理@CookieValue注解
  • RequestResponseBodyMethodProcessor:处理@RequestBody注解和@ResponseBody注解

以下是一个展示请求域参数传递的示例:

java 复制代码
@Controller
public class RequestController {

    // 取出请求域中的参数值的方法,并将请求参数传入到下一个请求
    @GetMapping("goto")
    public String goToPage(HttpServletRequest request) {
        request.setAttribute("msg", "这是从controller传入的参数");
        request.setAttribute("code", 200);
        return "forward:/success"; // 转发到/success请求
    }

    @GetMapping("/params")
    public String testParam(Map<String, Object> map,
                            Model model,
                            HttpServletRequest request,
                            HttpServletResponse response) {
        map.put("hello", "world666");
        model.addAttribute("world", "hello666");
        request.setAttribute("message", "HelloWorld");

        Cookie cookie = new Cookie("c1", "v1");
        cookie.setDomain("localhost");
        response.addCookie(cookie);

        return "forward:/success";
    }

    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute(value = "msg", required = false) String msg,
                       @RequestAttribute(value = "code", required = false) Integer code,
                       HttpServletRequest request) {
        Object msg1 = request.getAttribute("msg");
        Object code1 = request.getAttribute("code");

        Object hello = request.getAttribute("hello");
        Object world = request.getAttribute("world");
        Object message = request.getAttribute("message");

        Map<String, Object> map = new HashMap<>();
        map.put("reqMethod_msg", msg1); // 使用请求域中的参数
        map.put("annotation_msg", msg); // 使用参数注解传入的参数

        map.put("hello", hello);
        map.put("world", world);
        map.put("message", message);

        return map;
    }
}

在这个示例中,我们展示了如何通过HttpServletRequest设置请求域属性,以及如何通过@RequestAttribute注解或HttpServletRequest对象获取这些属性。当使用"forward"转发时,请求域中的属性会被保留到下一个请求中。

运行结果

我们来看一下cookie的值

三、自定义参数绑定

Person类

c 复制代码
package com.web.bean;

import lombok.Data;

import java.util.Date;

/**
 * @author cc
 * @date 2020/11/23-16:09
 */
@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}

宠物类

c 复制代码
package com.web.bean;

import lombok.Data;

@Data
public class Pet {
    private String name;
    private Integer age;
}

控制器接口方法

c 复制代码
@RestController
public class ParameterTestController {

    @PostMapping("/saveuser")
    public Person saveUser(Person person){
        return person;
    }

点击提交按钮

四、自定义参数绑定原理

1、核心机制:Web数据绑定

1. 核心组件:WebDataBinder

  • 功能:作为数据绑定的核心处理器,负责将HTTP请求参数(通常是字符串形式)的值绑定到目标JavaBean的对应属性上。
  • 工作流程
    1. 接收请求参数(String类型)。
    2. 利用内置的转换服务(ConversionService) 和其管理的转换器(Converters) ,将字符串数据转换为目标属性所需的复杂数据类型(如IntegerDateFile或自定义对象)。
    3. 将转换后的数据填充(封装)到JavaBean中。

2. 基础架构:通用转换服务(GenericConversionService)

  • 角色 :是类型转换系统的容器和调度中心。它是一个配置一次即可全局使用的服务。
  • 管理 :内部维护了一个转换器(Converter)集合。这个集合包含了Spring默认提供的众多转换器(用于处理基本类型、集合等)以及开发者注册的自定义转换器。
  • 职责 :当需要进行类型转换时,GenericConversionService会负责查找并调用合适的Converter来执行实际的转换工作。

2、自定义转换:原理与实现

1. 目的

解决框架默认转换能力不足的问题。当Spring无法自动将请求参数(String)转换为特定的、复杂的或自定义的目标类型时,就需要开发者介入,提供明确的转换规则。

2. 核心技术:Converter<S, T> 接口

  • 定义:一个函数式接口,是类型转换策略的抽象。
  • 泛型参数
    • S (Source):源类型,即需要被转换的数据类型(在Web场景中通常是String)。
    • T (Target):目标类型,即希望转换成的数据类型。
  • 核心方法T convert(S source)
    • 开发者需要实现此方法,编写具体的转换逻辑,例如解析字符串、构造对象、设置属性等。

3. 实现步骤

  1. 创建转换器 :定义一个类,实现Converter<S, T>接口,并在convert方法中完成从String到目标对象(如PersonOrder等)的转换逻辑。
  2. 注册转换器 :将自定义的Converter实例添加到Spring的ConversionService中,使其生效。
    • 配置方式 :可以通过Java配置类(使用@Bean注解)或XML配置文件进行注册。
  3. 自动应用 :注册成功后,当Spring MVC在处理数据绑定遇到相应类型的转换时,会自动调用这个自定义的Converter,无需在控制器中编写额外的解析代码。

3、核心关系图

复制代码
HTTP Request (带有String参数)
        |
        v
[WebDataBinder]  // 协调绑定过程
        |
        | 需要转换类型时调用
        v
[GenericConversionService]  // 转换服务管理中心
        |
        | 查找并委托给匹配的
        v
[Converter<S, T>]  // 执行实际转换工作的组件(内置的或自定义的)
        |
        v
目标类型的对象 (e.g., Integer, Date, CustomJavaBean)
        |
        v
填充到目标JavaBean中

小结

Spring通过 WebDataBinderGenericConversionService 构成了一套强大且可扩展的数据绑定与类型转换体系。其默认实现已经覆盖了大部分常见场景。对于特殊需求,开发者可以通过实现 Converter<S, T> 接口来定义精确的转换规则,并通过注册将其融入框架原有的转换流程中。这种设计既保证了开箱即用的便利性,又提供了高度的灵活性,是Spring MVC处理复杂Web参数绑定的关键机制。

总结

SpringBoot通过SpringMVC提供了完整且强大的Web请求参数处理机制,其核心在于灵活的参数绑定和类型转换体系。

在基础层面,框架提供了丰富的注解支持,包括@PathVariable、@RequestParam、@RequestHeader等,能够方便地提取URL路径、查询参数、请求头等各类信息。同时支持直接使用Servlet API对象和Map、Model等复杂参数类型,满足不同场景的需求。

参数处理的核心由HandlerAdapter组件实现,特别是RequestMappingHandlerAdapter负责解析方法参数。其内部通过HandlerMethodArgumentResolver策略接口,使用不同的解析器实现类来处理各类注解和参数类型。

对于自定义对象绑定,Spring提供了WebDataBinder和GenericConversionService组成的类型转换体系。WebDataBinder负责协调整个绑定过程,而GenericConversionService作为转换器容器,管理着包括默认转换器和自定义转换器在内的转换器集合。当默认转换能力不足时,开发者可以通过实现Converter<S, T>接口来定义特定类型的转换规则,并通过注册到转换服务中融入框架原有的转换流程。

这种分层设计使得SpringBoot既能提供开箱即用的便利性,又能通过扩展机制满足各种复杂的数据绑定需求,体现了框架的高度灵活性和可扩展性。

相关推荐
Dragon Wu19 分钟前
前端 下载后端返回的二进制excel数据
前端·javascript·html5
Q_Q51100828523 分钟前
python的校园研招网系统
开发语言·spring boot·python·django·flask·node.js·php
舒一笑25 分钟前
如何优雅统计知识库文件个数与子集下不同文件夹文件个数
后端·mysql·程序员
北海几经夏25 分钟前
React响应式链路
前端·react.js
IT果果日记26 分钟前
flink+dolphinscheduler+dinky打造自动化数仓平台
大数据·后端·flink
Java技术小馆37 分钟前
InheritableThreadLoca90%开发者踩过的坑
后端·面试·github
寒士obj1 小时前
Spring容器Bean的创建流程
java·后端·spring
晴空雨1 小时前
React Media 深度解析:从使用到 window.matchMedia API 详解
前端·react.js
一个有故事的男同学1 小时前
React性能优化全景图:从问题发现到解决方案
前端
探码科技1 小时前
2025年20+超实用技术文档工具清单推荐
前端