06-参数注解

附:什么是JavaBean?什么是POJO?

POJO (Plain Old Java Object) - 简单老式Java对象
指一个普通的Java类,不依赖于任何特定的框架、接口或父类。它就是一个最基本的、自带属性的数据容器。

// 这是一个典型的POJO
public class User {
    public String name; // 甚至可以是公共字段
    private int age;

    // 可以有任意方法,不强制要求有getter/setter
    public void celebrateBirthday() {
        this.age++;
    }
}

JavaBean
JavaBean是一种特殊的POJO,它遵循一套严格的编写规范:

  1. 有无参的公共构造函数(默认或无参构造)。
  2. 属性都是私有的(private)。
  3. 属性通过公共的getter和setter方法对外提供访问(遵循getXxx/setXxx命名规范)。
  4. 可序列化(实现java.io.Serializable接口)。
// 这是一个符合规范的JavaBean
public class User implements java.io.Serializable { // 1. 可序列化
    private String name; // 2. 属性私有
    private int age;

    public User() {} // 3. 有无参公共构造

    // 4. 公共的getter和setter
    public 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; }
}

在SpringMVC中的关系:
SpringMVC的自动参数绑定严重依赖JavaBean规范。因为它底层是通过反射调用setXxx()方法来给对象赋值的。如果你有一个POJO但没有提供setter方法,SpringMVC将无法自动为其属性注入值(除非字段是public的,但这不推荐)。所以,用于接收请求参数的类,最好被写成JavaBean,而不仅仅是一个POJO

SpringMVC 参数注解详解

注解是给谁看的?

这些注解是给 SpringMVC 框架 看的。当你编写控制器方法时,你通过这些注解向框架发出明确的指令,告诉它应该如何获取和处理HTTP请求中的各种数据。

框架通过 Java反射机制 在运行时读取这些注解,然后根据注解的指示执行相应的操作。

注解底层工作原理

SpringMVC内部有一个HandlerMethodArgumentResolver接口体系,不同的注解由不同的解析器实现:

  1. 框架检查控制器方法的每个参数
  2. 发现参数上的注解
  3. 找到对应的参数解析器
  4. 解析器根据注解配置从请求中提取数据
  5. 数据经过类型转换后传递给方法参数

各注解详解

1. @RequestParam - 从URL查询参数获取值

作用:从URL的问号后参数(?name=value)中提取值。

属性

  • value/name:参数名称(必需)
  • required:是否必须提供(默认true)
  • defaultValue:默认值(设置后required自动变为false)

示例

@GetMapping("/users")
public String getUser(
    @RequestParam("id") Long userId, // 明确指定参数名
    @RequestParam(value = "name", required = false) String userName, // 可选参数
    @RequestParam(value = "page", defaultValue = "1") int page // 带默认值
) {
    // 方法实现
}

底层模拟

// 伪代码:框架处理@RequestParam的过程
Object resolveRequestParam(HttpServletRequest request, RequestParam annotation, Class<?> paramType) {
    String paramName = annotation.value(); // 获取注解中指定的参数名
    boolean required = annotation.required();
    String defaultValue = annotation.defaultValue();
    
    // 从请求参数Map中获取值
    String paramValue = request.getParameter(paramName);
    
    if (paramValue == null) {
        if (required && defaultValue.isEmpty()) {
            throw new MissingServletRequestParameterException(paramName, paramType.getSimpleName());
        }
        paramValue = defaultValue;
    }
    
    // 类型转换(String -> 目标类型)
    return convertToType(paramValue, paramType);
}

2. @PathVariable - 从URL路径中获取值

作用:从REST风格的URL路径中提取变量值。

属性

  • value/name:路径变量名称(必需)
  • required:是否必须存在(默认true)

示例

@GetMapping("/users/{userId}/posts/{postId}")
public String getPost(
    @PathVariable("userId") Long userId, // 明确指定变量名
    @PathVariable Long postId // 省略value,默认使用参数名
) {
    // 方法实现
}

底层模拟

// 伪代码:框架处理@PathVariable的过程
Object resolvePathVariable(HttpServletRequest request, PathVariable annotation, Class<?> paramType) {
    String variableName = annotation.value(); // 获取路径变量名
    // 框架预先已经从URL模板"/users/{userId}"和实际路径"/users/123"中提取了变量
    Map<String, String> pathVariables = (Map<String, String>) request.getAttribute("pathVariables");
    
    String value = pathVariables.get(variableName);
    if (value == null && annotation.required()) {
        throw new IllegalArgumentException("Path variable '" + variableName + "' is required");
    }
    
    return convertToType(value, paramType);
}

3. @RequestBody - 从请求体中获取数据

作用:将HTTP请求体中的数据绑定到方法参数上,常用于JSON/XML数据。

属性

  • required:是否必须提供请求体(默认true)

示例

@PostMapping("/users")
public User createUser(@RequestBody User user) {
    // 直接将JSON请求体转换为User对象
    return userService.save(user);
}

底层模拟

// 伪代码:框架处理@RequestBody的过程
Object resolveRequestBody(HttpServletRequest request, RequestBody annotation, Class<?> paramType) {
    if (annotation.required() && request.getContentLength() == 0) {
        throw new HttpMessageNotReadableException("Required request body is missing");
    }
    
    // 获取适合 Content-Type 的消息转换器
    HttpMessageConverter converter = findConverter(request.getContentType());
    
    // 读取并转换请求体
    return converter.read(paramType, request);
}

4. @RequestHeader - 从HTTP头中获取值

作用:从HTTP请求头中提取值。

属性

  • value/name:请求头名称(必需)
  • required:是否必须存在(默认true)
  • defaultValue:默认值

示例

@GetMapping("/info")
public String getInfo(
    @RequestHeader("User-Agent") String userAgent, // 获取浏览器信息
    @RequestHeader(value = "X-Custom-Header", defaultValue = "default") String customHeader
) {
    // 方法实现
}

底层模拟

// 伪代码:框架处理@RequestHeader的过程
Object resolveRequestHeader(HttpServletRequest request, RequestHeader annotation, Class<?> paramType) {
    String headerName = annotation.value();
    String headerValue = request.getHeader(headerName);
    
    if (headerValue == null) {
        if (annotation.required()) {
            throw new MissingRequestHeaderException(headerName, paramType);
        }
        headerValue = annotation.defaultValue();
    }
    
    return convertToType(headerValue, paramType);
}

5. @CookieValue - 从Cookie中获取值

作用:从HTTP Cookie中提取值。

属性

  • value/name:Cookie名称(必需)
  • required:是否必须存在(默认true)
  • defaultValue:默认值

示例

@GetMapping("/profile")
public String getProfile(@CookieValue("JSESSIONID") String sessionId) {
    // 使用sessionId获取用户信息
    return userService.getProfile(sessionId);
}

底层模拟

// 伪代码:框架处理@CookieValue的过程
Object resolveCookieValue(HttpServletRequest request, CookieValue annotation, Class<?> paramType) {
    String cookieName = annotation.value();
    Cookie[] cookies = request.getCookies();
    
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if (cookieName.equals(cookie.getName())) {
                return convertToType(cookie.getValue(), paramType);
            }
        }
    }
    
    if (annotation.required()) {
        throw new MissingCookieValueException(cookieName, paramType);
    }
    
    return convertToType(annotation.defaultValue(), paramType);
}

总结

这些注解的本质是提供给SpringMVC框架的指令,告诉它:

  1. 数据来源:从哪里获取数据(查询参数、路径、请求体、头部、Cookie)
  2. 标识符:用什么Key/Name来查找数据
  3. 处理规则:找不到数据时怎么办(报错、使用默认值、允许为空)

通过这种声明式的方式,你不需要手动编写繁琐的请求数据处理代码,而是通过注解告诉框架你的需求,框架会自动完成这些工作。这不仅减少了代码量,也提高了代码的可读性和可维护性。

posted on 2025-09-14 20:22  笨忠  阅读(14)  评论(0)    收藏  举报