附:什么是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,它遵循一套严格的编写规范:
- 有无参的公共构造函数(默认或无参构造)。
- 属性都是私有的(private)。
- 属性通过公共的getter和setter方法对外提供访问(遵循
getXxx/setXxx命名规范)。 - 可序列化(实现
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. @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框架的指令,告诉它:
- 数据来源:从哪里获取数据(查询参数、路径、请求体、头部、Cookie)
- 标识符:用什么Key/Name来查找数据
- 处理规则:找不到数据时怎么办(报错、使用默认值、允许为空)
通过这种声明式的方式,你不需要手动编写繁琐的请求数据处理代码,而是通过注解告诉框架你的需求,框架会自动完成这些工作。这不仅减少了代码量,也提高了代码的可读性和可维护性。
浙公网安备 33010602011771号