Spring核心接口:HandlerMethodArgumentResolver参数解析器

26 篇文章 0 订阅
18 篇文章 0 订阅

Spring是一个广泛使用的Java框架,其中一个重要的特性是对HTTP请求的处理。在处理HTTP请求时,Spring提供了许多工具和机制来帮助开发人员更容易地处理请求参数。其中一个机制就是参数解析器。本文将全面介绍Spring中的参数解析器,包括其工作原理、不同类型的参数解析器、如何自定义参数解析器以及如何配置参数解析器。HandlerMethodArgumentResolver

1. 什么是参数解析器

在Spring中,参数解析器是一个接口,它负责将HTTP请求中的参数解析为控制器方法的参数。Spring提供了许多内置的参数解析器,用于处理不同类型的请求参数,例如查询参数、路径变量、请求头、请求体等。开发人员还可以自定义参数解析器,以处理特定的请求参数。

2. Spring中的参数解析器工作原理

当Spring接收到一个HTTP请求时,它会根据请求的URL找到相应的控制器方法。然后,Spring会将请求参数传递给控制器方法。在这个过程中,Spring会使用参数解析器来解析请求参数。

具体来说,Spring会按照以下步骤处理请求参数:

  1. 检查控制器方法的参数类型,以确定应该使用哪个参数解析器。
  2. 根据请求参数的类型和位置,选择一个适当的参数解析器。
  3. 调用参数解析器的resolveArgument方法,将请求参数解析为控制器方法的参数。
  4. 将解析后的参数传递给控制器方法。

3. Spring中内置的参数解析器

Spring提供了许多内置的参数解析器,用于处理不同类型的请求参数。以下是一些常见的参数解析器:

3.1 RequestParamMethodArgumentResolver

RequestParamMethodArgumentResolver用于解析请求参数中的查询参数。它支持简单类型(如int、long、String等)和复杂类型(如POJO等)的查询参数。以下是一个示例:

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@RequestParam("name") String name, @RequestParam("age") Integer age) {
    // ...
}

在这个示例中,Spring会使用RequestParamMethodArgumentResolver来解析name和age查询参数,并将它们传递给getUsers方法。

3.2 PathVariableMethodArgumentResolver

PathVariableMethodArgumentResolver用于解析请求参数中的路径变量。它支持简单类型和复杂类型的路径变量。以下是一个示例:

@GetMapping("/users/{userId}")
public ResponseEntity<User> getUser(@PathVariable("userId") Long userId) {
    // ...
}

在这个示例中,Spring会使用PathVariableMethodArgumentResolver来解析userId路径变量,并将它传递给getUser方法。

3.3 RequestHeaderMethodArgumentResolver

RequestHeaderMethodArgumentResolver用于解析请求头中的参数。它支持简单类型和复杂类型的请求头参数。以下是一个示例:

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@RequestHeader("X-AUTH-TOKEN") String authToken) {
    // ...
}

在这个示例中,Spring会使用RequestHeaderMethodArgumentResolver来解析X-AUTH-TOKEN请求头参数,并将它传递给getUsers方法。

3.4 RequestBodyMethodArgumentResolver

RequestBodyMethodArgumentResolver用于解析请求体中的参数。它支持简单类型和复杂类型的请求体参数。以下是一个示例:

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // ...
}

在这个示例中,Spring会使用RequestBodyMethodArgumentResolver来解析请求体中的JSON数据,并将其映射为User对象,然后传递给createUser方法。

3.5 ServletModelAttributeMethodProcessor

ServletModelAttributeMethodProcessor用于将请求参数绑定到模型对象中。它支持简单类型和复杂类型的模型属性。以下是一个示例:

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(UserQuery query, Model model) {
    // ...
}

在这个示例中,Spring会使用ServletModelAttributeMethodProcessor来将请求参数绑定到UserQuery对象中,并将其添加到模型对象中。

3.6 PrincipalMethodArgumentResolver

PrincipalMethodArgumentResolver用于解析当前认证用户的主体信息。它支持Authentication和Principal类型的参数。以下是一个示例:

@GetMapping("/users/me")
public ResponseEntity<User> getCurrentUser(Principal principal) {
    // ...
}

在这个示例中,Spring会使用PrincipalMethodArgumentResolver来解析当前认证用户的主体信息,并将其传递给getCurrentUser方法。

4. 自定义参数解析器

除了Spring提供的内置参数解析器外,开发人员还可以自定义参数解析器,以处理特定的请求参数。自定义参数解析器需要实现HandlerMethodArgumentResolver接口,并在配置类中将其添加到WebMvcConfigurer中。以下是一个示例:

public class CustomMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 检查参数是否为特定类型
        return parameter.getParameterType().isAssignableFrom(CustomType.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // 从请求参数中解析CustomType对象
        String customParam = webRequest.getParameter("customParam");
        CustomType customType = CustomType.valueOf(customParam);
        return customType;
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new CustomMethodArgumentResolver());
    }
}

在这个示例中,CustomMethodArgumentResolver用于解析请求参数中的customParam参数,并将其映射为CustomType枚举类型。然后,它被添加到WebMvcConfigurer中,以便Spring在处理请求时使用它。

5. 配置参数解析器

Spring允许开发人员通过配置来自定义参数解析器的行为。以下是一些常见的配置选项:

5.1 启用和禁用参数解析器

开发人员可以通过配置来启用和禁用特定的参数解析器。以下是一个示例:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // 禁用RequestParamMethodArgumentResolver
        argumentResolvers.removeIf(resolver -> resolver instanceof RequestParamMethodArgumentResolver);

        // 添加自定义参数解析器
        argumentResolvers.add(new CustomMethodArgumentResolver());
    }
}

在这个示例中,RequestParamMethodArgumentResolver被禁用,并添加了一个自定义参数解析器。

5.2 自定义参数解析器的顺序

开发人员可以通过配置来自定义参数解析器的顺序。以下是一个示例:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // 添加自定义参数解析器
        argumentResolvers.add(new CustomMethodArgumentResolver());

        // 将自定义参数解析器移动到第一个位置
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(argumentResolvers);
        resolvers.removeIf(resolver -> resolver instanceof CustomMethodArgumentResolver);
        resolvers.add(0, new CustomMethodArgumentResolver());
        argumentResolvers.clear();
        argumentResolvers.addAll(resolvers);
    }
}

在这个示例中,自定义参数解析器被移动到了第一个位置,以便Spring优先使用它。

5.3 配置请求参数的数据绑定

开发人员可以通过配置来自定义请求参数的数据绑定行为。以下是一个示例:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 注册自定义日期格式化器
        registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
    }
}

在这个示例中,注册了一个自定义日期格式化器,以便Spring在绑定请求参数时使用它。

6. 结论

本文介绍了Spring中的参数解析器,包括其工作原理、不同类型的参数解析器、如何自定义参数解析器以及如何配置参数解析器。通过使用参数解析器,开发人员可以更容易地处理HTTP请求中的参数,从而更快速地开发Web应用程序。

7.原理解析

这里以PathVariableMethodArgumentResolver为例。

PathVariableMethodArgumentResolver 是 Spring 框架中用来处理带 @PathVariable 注解的方法参数解析的内置解析器。Spring 框架内部已经提供了 PathVariableMethodArgumentResolver 的实现,开发者通常不需要自己实现它,但是了解其背后的机制会对理解 Spring MVC 的工作方式有所帮助。下面我将提供一个类似 PathVariableMethodArgumentResolver 功能的简化版本的自定义参数解析器的实现。

首先,我们需要创建一个实现了 HandlerMethodArgumentResolver 接口的类,并且实现 supportsParameterresolveArgument 方法:

import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Component
public class CustomPathVariableMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 检查参数是否有 @PathVariable 注解
        return parameter.hasParameterAnnotation(PathVariable.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  HttpServletRequest request, WebDataBinderFactory binderFactory)
            throws Exception {
        // 获取 @PathVariable 注解的 value,即路径变量名称
        PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
        Assert.state(pathVariable != null, "No PathVariable annotation");
        String pathVariableName = pathVariable.value();

        // 从请求中获取路径变量的 Map
        Map<String, String> uriTemplateVars =
            (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        if (!uriTemplateVars.containsKey(pathVariableName)) {
            throw new ServletRequestBindingException("Missing path variable '" + pathVariableName + "' for method parameter type [" + parameter.getParameterType() + "]");
        }

        // 获取路径变量的值
        String pathVariableValue = uriTemplateVars.get(pathVariableName);
        // 这里你可以根据需要将字符串值转换为方法参数的实际类型
        // 例如,你可以使用 binderFactory 来转换值,或者直接调用适当的转换方法
        // 简单起见,这里的代码假设方法参数类型是 String

        return pathVariableValue;
    }
}

上面这个简化的自定义参数解析器类 CustomPathVariableMethodArgumentResolver 通过检查方法参数是否使用了 @PathVariable 注解来决定是否支持该参数的解析。resolveArgument 方法利用请求中的 HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE 属性来获取所有的路径变量,并解析出需要的那个参数值。

在实际的 Spring 应用上下文中,你需要将这个自定义参数解析器注册为一个 Bean。你可以通过添加 @Component 注解(我们这里已经添加了)使得 Spring 在启动时自动检测和注册它,或者你可以在配置类中显式地声明它:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private CustomPathVariableMethodArgumentResolver customPathVariableMethodArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(customPathVariableMethodArgumentResolver);
    }
}

通过这种方式,当 Spring MVC 遇到带有 @PathVariable 注解的控制器方法参数时,它将使用你定义的 CustomPathVariableMethodArgumentResolver 来解析这个参数。

在Spring MVC中,HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE是一个在请求处理过程中使用的常量,它代表了映射在URL路径中的模板变量的Map对象。这个Map对象包含了哪些以{}括起来的路径变量,并且它们的值是在请求的URL中解析得到的。

例如,假设你有一个URL路径模式如下:

/user/{userId}/profile/{profileId}

当一个请求例如/user/42/profile/7匹配到上面的路径模式时,Spring MVC的一个HandlerMapping会解析这个URL,并将路径变量userIdprofileId及其对应的值427放入Map中。然后,这个Map会被存储在请求的属性中,属性名为HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE

在请求处理流程后续的某个阶段,如果你的controller方法需要那个路径变量,PathVariableMethodArgumentResolver或者你自己定义的类似实现将会使用这个Map来获取路径变量的值。

以下是一个包含路径变量的@GetMapping方法例子:

@GetMapping("/user/{userId}/profile/{profileId}")
public String getUserProfile(@PathVariable String userId,
                             @PathVariable String profileId) {
    // ...
}

当上面的方法被调用时,Spring会使用PathVariableMethodArgumentResolver解析器来查找HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE属性里的路径变量,取出userIdprofileId各自对应的值,并将它们注入方法参数中。

这个属性是Spring内部使用的,作为开发者通常我们不直接与它打交道,除非你正在自定义路径变量解析逻辑或者处理一些高级的路由功能。在正常使用Spring MVC时,@PathVariable注解和参数解析机制已经足够满足大多数的URL路径变量解析需要了。

8. 参考资料

  1. Spring Framework Reference Documentation: https://docs.spring.io/spring-framework/docs/current/reference/html/
  2. Spring Web MVC: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
  3. HandlerMethodArgumentResolver: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html
  4. WebMvcConfigurer: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html
  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

five-five

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值