SpringMVC提供了各种姿势的http参数解析支持,从前面的GET/POST参数解析篇也可以看到,加一个@RequsetParam注解就可以将方法参数与http参数绑定,看到这时自然就会好奇这是怎么做到的,我们能不能自己定义一种参数解析规则呢?
本文将介绍如何实现自定义的参数解析,并让其生效
I. 环境搭建 首先得搭建一个web应用才有可能继续后续的测试,借助SpringBoot搭建一个web应用属于比较简单的活;
创建一个maven项目,pom文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.1.7</version > <relativePath /> </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参数的
既然是参数解析,所以肯定是在方法调用之前就会被触发,在Spring中,负责将http参数与目标方法参数进行关联的,主要是借助org.springframework.web.method.support.HandlerMethodArgumentResolver类来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @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 定义这个注解,主要就是用于表明,带有这个注解的参数,希望可以使用我们自定义的参数解析器来解析;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Target (ElementType.PARAMETER)@Retention (RetentionPolicy.RUNTIME)@Documented public @interface ListParam { @AliasFor ("name" ) String value () default "" ; @AliasFor ("value" ) String name () default "" ; }
b. 参数解析器ListHandlerMethodArgumentResolver 接下来就是自定义的参数解析器了,需要实现接口HandlerMethodArgumentResolver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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才能生效,一个简单的方法如下
1 2 3 4 5 6 7 8 9 10 11 12 @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服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RestController @RequestMapping (path = "get" )public class ParamGetRest { @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. 项目&相关博文
1. 一灰灰Blog 尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏