HandlerMethodArgumentResolver(二):Map参数类型和固定参数类型【享学Spring MVC】(上)

简介: HandlerMethodArgumentResolver(二):Map参数类型和固定参数类型【享学Spring MVC】(上)

前言


上文 介绍了Spring MVC用于处理入参的处理器:HandlerMethodReturnValueHandler它的作用,以及介绍了最为常用的两个参数处理器子类:PathVariableMethodArgumentResolver和RequestParamMethodArgumentResolver。由于该体系的重要以及庞大,本文将接着继续讲解~


第一类:基于Name(续)


RequestHeaderMethodArgumentResolver

@RequestHeader注解,可以把Request请求header部分的值绑定到方法的参数上。


public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
  // 必须标注@RequestHeader注解,并且不能,不能,不能是Map类型
  // 有的小伙伴会说:`@RequestHeader Map headers`这样可以接收到所有的请求头啊
  // 其实不是本类的功劳,是`RequestHeaderMapMethodArgumentResolver`的作用
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return (parameter.hasParameterAnnotation(RequestHeader.class) &&
        !Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
  }
  // 理解起来很简单:可以单值,也可以List/数组
  @Override
  @Nullable
  protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    String[] headerValues = request.getHeaderValues(name);
    if (headerValues != null) {
      return (headerValues.length == 1 ? headerValues[0] : headerValues);
    } else {
      return null;
    }
  }
}


此处理器能处理的是我们这么来使用:


    @ResponseBody
    @GetMapping("/test")
    public Object test(@RequestHeader("Accept-Encoding") String encoding,
                       @RequestHeader("Accept-Encoding") List<String> encodingList) {
        System.out.println(encoding);
        System.out.println(encodingList);
        return encoding;
    }


请求头截图:


image.png


结果打印(集合封装成功了,证明逗号分隔是可以被封装成集合/数组的):

gzip, deflate, br
[gzip, deflate, br]


Tip:注解指定的value值(key值)是区分大小写的


RequestAttributeMethodArgumentResolver


处理必须标注有@RequestAttribute注解的参数,原理说这一句话就够了。


return request.getAttribute(name, RequestAttributes.SCOPE_REQUEST);


SessionAttributeMethodArgumentResolver


同上(注解不一样,scope不一样而已)


AbstractCookieValueMethodArgumentResolver(抽象类)


对解析标注有@CookieValue的做了一层抽象,子类负责从request里拿值(该抽象类不合请求域绑定)。

public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
  ...
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(CookieValue.class);
  } 
  @Override
  protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
    throw new MissingRequestCookieException(name, parameter);
  }
  ... // 并木有实现核心resolveName方法
}


ServletCookieValueMethodArgumentResolver


指定了从HttpServletRequest去拿cookie值。

public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValueMethodArgumentResolver {
  private UrlPathHelper urlPathHelper = new UrlPathHelper();
  ...
  public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
    this.urlPathHelper = urlPathHelper;
  }
  @Override
  @Nullable
  protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    // 工具方法,底层是:request.getCookies()
    Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
    // 如果用javax.servlet.http.Cookie接受值,就直接返回了
    if (Cookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
      return cookieValue;
    } else if (cookieValue != null) { // 否则返回cookieValue
      return this.urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
    } else {
      return null;
    }
  }
}


一般我们这么来用:

    @ResponseBody
    @GetMapping("/test")
    public Object test(@CookieValue("JSESSIONID") Cookie cookie,
                       @CookieValue("JSESSIONID") String cookieValue) {
        System.out.println(cookie);
        System.out.println(cookieValue);
        return cookieValue;
    }


手动设置一个cookie值,然后请求

image.png


控制台打印如下:

javax.servlet.http.Cookie@401ef395
123456



Tips:在现在restful风格下,cookie使用得是很少的了。一般用于提升用户体验方面~


MatrixVariableMethodArgumentResolver


标注有@MatrixVariable注解的参数的处理器。Matrix:矩阵,这个注解是Spring3.2新提出来的,增强Restful的处理能力(配合@PathVariable使用),比如这类URL的解析就得靠它:/owners/42;q=11/pets/21;s=23;q=22。


关于@MatrixVariable它的使用案例,我找了两篇靠谱文章给你参考:

参考一

参考二


// @since 3.2
public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
  // @MatrixVariable注解是必须的。然后技能处理普通类型,也能处理Map
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
      return false;
    }
    if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
      MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
      return (matrixVariable != null && StringUtils.hasText(matrixVariable.name()));
    }
    return true;
  }
  ...
}


ExpressionValueMethodArgumentResolver

它用于处理标注有@Value注解的参数。对于这个注解我们太熟悉不过了,没想到在web层依旧能发挥作用。本文就重点来会会它~


通过@Value让我们在配置文件里给参数赋值,在某些特殊场合(比如前端不用传,但你想给个默认值,这个时候用它也是一种方案)


说明:这就相当于在Controller层使用了@Value注解,其实我是不太建议的。因为@Value建议还是只使用在业务层~


// @since 3.1
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
  // 唯一构造函数  支持占位符、SpEL
  public ExpressionValueMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory) {
    super(beanFactory);
  }
  //必须标注有@Value注解
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(Value.class);
  }
  @Override
  protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
    Value ann = parameter.getParameterAnnotation(Value.class);
    return new ExpressionValueNamedValueInfo(ann);
  }
  private static final class ExpressionValueNamedValueInfo extends NamedValueInfo {
    // 这里name传值为固定值  因为只要你的key不是这个就木有问题
    // required传固定值false
    // defaultValue:取值为annotation.value() --> 它天然支持占位符和SpEL嘛
    private ExpressionValueNamedValueInfo(Value annotation) {
      super("@Value", false, annotation.value());
    }
  }
  // 这里恒返回null,因此即使你的key是@Value,也是不会采纳你的传值的哟~
  @Override
  @Nullable
  protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
    // No name to resolve
    return null;
  }
}


根本原理其实只是利用了defaultValue支持占位符和SpEL的特性而已。给个使用示例:


// 在MVC子容器中导入外部化配置
@Configuration
@PropertySource("classpath:my.properties") // 此处有键值对:test.myage = 18
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter { ... }
    @ResponseBody
    @GetMapping("/test")
    public Object test(@Value("#{T(Integer).parseInt('${test.myage:10}') + 10}") Integer myAge) {
        System.out.println(myAge);
        return myAge;
    }


请求:/test,打印:28。

注意:若你写成@Value("#{'${test.myage:10}' + 10},那你得到的答案是:1810(成字符串拼接了)。




目录
打赏
0
0
0
0
37
分享
相关文章
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
85 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
105 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
77 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
85 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
63 0
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
188 29
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
91 4
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
171 0
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
文章解释了SpringMVC的概念和各部分功能,探讨了应用分层的原因和具体实施的三层架构,以及SpringMVC与三层架构之间的关系和联系。
112 1
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
110 1

热门文章

最新文章