SpringMVC 提供了各种姿势的 http 参数解析支持, 从前面的 GET/POST 参数解析篇也可以看到, 加一个 @RequsetParam 注解就可以将方法参数与 http 参数绑定, 看到这时自然就会好奇这是怎么做到的, 我们能不能自己定义一种参数解析规则呢?
本文将介绍如何实现自定义的参数解析, 并让其生效
I. 环境搭建
首先得搭建一个 web 应用才有可能继续后续的测试, 借助 SpringBoot 搭建一个 Web 应用属于比较简单的活;
创建一个 maven 项目, pom 文件如下
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.7</version>
- <relativePath/> <!-- lookup parent from update -->
- </parent>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
- <java.version>1.8</java.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-Web</artifactId>
- </dependency>
- </dependencies>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
- <repositories>
- <repository>
- <id>spring-milestones</id>
- <name>Spring Milestones</name>
- <url>https://repo.spring.io/milestone</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- </repositories>
II. 自定义参数解析器
对于如何自定义参数解析器, 一个较推荐的方法是, 先搞清楚 springmvc 接收到一个请求之后完整的处理链路, 然后再来看在什么地方, 什么时机, 来插入自定义参数解析器, 无论是从理解还是实现都会简单很多. 遗憾的是, 本篇主要目标放在的是使用角度, 所以这里只会简单的提一下参数解析的链路, 具体的深入留待后续的源码解析
1. 参数解析链路
http 请求流程图, 来自 SpringBoot 是如何解析 HTTP 参数的 https://www.jianshu.com/p/bf3537334e76
既然是参数解析, 所以肯定是在方法调用之前就会被触发, 在 Spring 中, 负责将 http 参数与目标方法参数进行关联的, 主要是借助 org.springframework.Web.method.support.HandlerMethodArgumentResolver 类来实现
- /**
- * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
- * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
- */
- @Override
- @Nullable
- public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
- NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
- HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
- if (resolver == null) {
- throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
- }
- return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
- }
上面这段核心代码来自 org.springframework.Web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument , 主要作用就是获取一个合适的 HandlerMethodArgumentResolver , 实现将 http 参数 ( webRequest ) 映射到目标方法的参数上( parameter )
所以说, 实现自定义参数解析器的核心就是实现一个自己的 HandlerMethodArgumentResolver
2. HandlerMethodArgumentResolver
实现一个自定义的参数解析器, 首先得有个目标, 我们在 get 参数解析篇里面, 当时遇到了一个问题, 当传参为数组时, 定义的方法参数需要为数组, 而不能是 List, 否则无法正常解析; 现在我们则希望能实现这样一个参数解析, 以支持上面的场景
为了实现上面这个小目标, 我们可以如下操作
a. 自定义注解 ListParam
定义这个注解, 主要就是用于表明, 带有这个注解的参数, 希望可以使用我们自定义的参数解析器来解析;
- @Target(ElementType.PARAMETER)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ListParam {
- /**
- * Alias for {@link #name}.
- */
- @AliasFor("name") String value() default "";
- /**
- * The name of the request parameter to bind to.
- *
- * @since 4.2
- */
- @AliasFor("value") String name() default "";
- }
b. 参数解析器 ListHandlerMethodArgumentResolver
接下来就是自定义的参数解析器了, 需要实现接口 HandlerMethodArgumentResolver
- public class ListHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
- @Override
- public boolean supportsParameter(MethodParameter parameter) {
- return parameter.hasParameterAnnotation(ListParam.class);
- }
- @Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
- NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
- ListParam param = parameter.getParameterAnnotation(ListParam.class);
- if (param == null) {
- throw new IllegalArgumentException(
- "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
- }
- String name = "".equalsIgnoreCase(param.name()) ? param.value() : param.name();
- if ("".equalsIgnoreCase(name)) {
- name = parameter.getParameter().getName();
- }
- String ans = webRequest.getParameter(name);
- if (ans == null) {
- return null;
- }
- String[] cells = StringUtils.split(ans, ",");
- return Arrays.asList(cells);
- }
- }
上面有两个方法:
supportsParameter 就是用来表明这个参数解析器适不适用
实现也比较简单, 就是看参数上有没有前面定义的 ListParam 注解
resolveArgument 这个方法就是实现将 http 参数粗转换为目标方法参数的具体逻辑
上面主要是为了演示自定义参数解析器的过程, 实现比较简单, 默认只支持 List<String>
3. 注册
上面虽然实现了自定义的参数解析器, 但是我们需要把它注册到 HandlerMethodArgumentResolver 才能生效, 一个简单的方法如下
- @SpringBootApplication
- public class Application extends WebMvcConfigurationSupport {
- @Override
- protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
- argumentResolvers.add(new ListHandlerMethodArgumentResolver());
- }
- public static void main(String[] args) {
- SpringApplication.run(Application.class);
- }
- }
4. 测试
为了验证我们的自定义参数解析器 ok, 我们开两个对比的 REST 服务
- @RestController
- @RequestMapping(path = "get")
- public class ParamGetRest {
- /**
- * 自定义参数解析器
- *
- * @param names
- * @param age
- * @return
- */
- @GetMapping(path = "self")
- public String selfParam(@ListParam(name = "names") List<String> names, Integer age) {
- return names + "| age=" + age;
- }
- @GetMapping(path = "self2")
- public String selfParam2(List<String> names, Integer age) {
- return names + "| age=" + age;
- }
- }
演示 demo 如下, 添加了 ListParam 注解的可以正常解析, 没有添加注解的会抛异常
II. 其他
0. 项目 & 相关博文
190824-SpringBoot 系列教程 Web 篇之 Get 请求参数解析姿势汇总
190828-SpringBoot 系列教程 Web 篇之 Post 请求参数解析姿势汇总
工程: https://github.com/liuyueyi/spring-boot-demo
项目:
1. 一灰灰 Blog
尽信书则不如, 以上内容, 纯属一家之言, 因个人能力有限, 难免有疏漏和错误之处, 如发现 bug 或者有更好的建议, 欢迎批评指正, 不吝感激
下面一灰灰的个人博客, 记录所有学习和工作中的博文, 欢迎大家前去逛逛
一灰灰 Blog 个人博客 https://blog.hhui.top/
一灰灰 Blog-Spring 专题博客 http://spring.hhui.top/
打赏 如果觉得我的文章对您有帮助, 请随意打赏.
来源: http://www.tuicool.com/articles/qmyEzqB