关键重写方法:resolveArgument
resolveArgument
方法是 Spring MVC 框架中一个非常核心且强大的接口方法。
它来自于 HandlerMethodArgumentResolver 接口。简单来说,这个方法的作用是:
在 Spring MVC 调用你的 Controller(控制器)方法之前,用来准备和解析该方法所需要的参数值。
你可以把它理解成一个“参数解析器”或“参数加工厂”。当一个 HTTP 请求到达你的 Controller 方法时,Spring MVC 会检查这个方法的每一个参数,然后找到能够处理该类型参数的 HandlerMethodArgumentResolver,并调用其 resolveArgument 方法来生成这个参数的实例,最后将这个实例传递给你的 Controller 方法。
工作流程解析
我们来看一个典型的 Spring MVC Controller 方法:
@RestController
public class UserController {@GetMapping("/users/{id}") public User getUserById(@PathVariable("id") Long userId, @RequestParam("details") boolean showDetails, @CurrentUser CustomUser currentUser) { // 假设 @CurrentUser 是一个我们自定义的注解 // ... 方法体 return userService.findById(userId, showDetails, currentUser); }
}
当一个请求(例如 /users/123?details=true)进来时,Spring MVC 的处理流程大致如下:
- 分派请求:DispatcherServlet 接收到请求,并找到应该由 UserController 的 getUserById 方法来处理。
- 参数解析阶段:在真正执行 getUserById 方法之前,Spring MVC 会分析它的参数列表:Long userId, boolean showDetails, CustomUser currentUser。
- 寻找解析器:
- 对于 userId 参数,因为它有 @PathVariable 注解,Spring 会找到 PathVariableMethodArgumentResolver 来处理它。这个解析器会从 URL 路径中提取 "123",转换成 Long 类型,然后返回 123L。
- 对于 showDetails 参数,因为它有 @RequestParam 注解,Spring 会找到 RequestParamMethodArgumentResolver。它会从请求的查询参数中提取 "true",转换成 boolean 类型,然后返回 true。
- 对于 currentUser 参数,因为它有一个自定义的 @CurrentUser 注解,Spring 内置的解析器不认识它。这时,就需要我们自己实现一个 HandlerMethodArgumentResolver,并在这个实现类中重写 resolveArgument 方法。这个方法会负责从(比如)Session、Token 或者数据库中获取当前登录的用户信息,并创建一个 CustomUser 对象返回。
- 执行方法:当所有参数都被对应的解析器成功解析并生成实例后,Spring MVC 才会把这些准备好的值(123L, true, 和一个 CustomUser 对象)作为参数,去调用 getUserById 方法。
resolveArgument 方法的四个参数详解
现在我们回过头来看这个方法的签名,理解了上面的流程,这四个参数的作用就清晰了:
public Object resolveArgument( MethodParameter parameter, // 1\. 当前需要解析的参数信息 ModelAndViewContainer mavContainer, // 2\. MVC模型和视图容器 NativeWebRequest webRequest, // 3\. 底层的HTTP请求对象 WebDataBinderFactory binderFactory // 4\. 数据绑定和类型转换工厂
) throws Exception;
- MethodParameter parameter:
- 作用:它封装了当前需要解析的目标方法参数的所有信息。
- 你能得到什么:
- 参数的类型(例如 CustomUser.class)。
- 参数的名称(例如 "currentUser")。
- 参数上的注解(例如 @CurrentUser)。
- 参数所在的方法、类的所有信息。
- 用途:这是最重要的输入。你的解析逻辑首先要通过它来判断这个参数是不是你想要处理的类型(通常通过检查参数类型或它上面的特定注解)。
- ModelAndViewContainer mavContainer:
- 作用:它包含了当前请求的 Model(数据模型)和 View(视图)信息。
- 你能得到什么:可以访问和修改即将传递给视图的 Model 数据。
- 用途:通常在解析普通参数时用得不多,但在需要与视图层交互的场景下很有用。例如,可以在解析参数的同时,向 Model 中添加一些所有视图都需要的公共属性。
- NativeWebRequest webRequest:
- 作用:这是一个非常关键的参数,它提供了对底层 HTTP 请求的访问能力。
- 你能得到什么:
- 请求头(Headers)。
- 请求参数(Parameters)。
- 请求体(Body,但通常被其他解析器先读取)。
- Session 属性。
- Request 属性。
- 底层的 HttpServletRequest 和 HttpServletResponse 对象。
- 用途:绝大多数自定义参数解析器都需要从这个对象里提取数据。比如,从请求头里获取 Authorization Token,或者从 Session 中获取用户信息。
- WebDataBinderFactory binderFactory:
- 作用:一个工厂,用于创建 WebDataBinder 实例。WebDataBinder 是 Spring 中用于实现请求参数到JavaBean对象属性绑定和类型转换的核心组件。
- 你能得到什么:可以创建一个 WebDataBinder 来对从请求中获取的原始数据(通常是字符串)进行类型转换、格式化和校验。
- 用途:当你的参数是一个复杂的对象,并且需要将请求中的多个参数绑定到这个对象的字段上时,这个工厂会非常有用。
总结
resolveArgument
方法是 Spring MVC 中实现自定义参数解析的核心扩展点。
通过实现 HandlerMethodArgumentResolver
接口并重写此方法,你可以:
- 创建自定义注解(如 @CurrentUser),并为其提供解析逻辑。
- 简化 Controller 方法签名,将一些通用的、重复的参数获取逻辑(如获取当前登录用户、解析特定格式的请求头等)封装到解析器中。
- 实现与业务无关的参数预处理,让 Controller 更专注于业务逻辑本身。
它是一个典型的“策略模式”应用,Spring MVC 定义了“如何解析参数”的策略接口,而具体的内置实现和用户自定义实现则是不同的策略。