减少10%的代码:自定义参数解析器真的很强大,你不来了解一下?
👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事上“练” 《互联网高频面试题》:面朝简历学习,春暖花开 《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题 《精进 Java 学习指南》:系统学习,互联网主流技术栈 《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。
功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:
Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud 视频教程:https://doc.iocoder.cn 【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本
Part1 前言
springMvc中提供了很多好用的参数绑定的方式方法,那枚举呢?或者参数的值是一个json字符串的时候?你是怎么处理的?下面分享一下我的处理方式。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro 视频教程:https://doc.iocoder.cn/video/
Part2 枚举
普通的枚举类型,比如单列值的那种:one ,two... 。这种事不需要特殊处理的,我们是可以直接接收值并绑定数据的。
要是下面这种枚举类型呢?而且我们的参数传递的是:0,1这种数字,方法参数是枚举类型。spring还能帮我们自动绑定参数嘛?
public enum StatusEnum {
online(1),
offline(0);
private Integer value;
StatusEnum(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
}
这时候spring就无法自动帮我们绑定参数了,报如下错误:
1 实现方式
通过定时枚举参数注解来标记参数:这是一个枚举类型的参数。 通过自定义参数解析器来分析枚举参数注解,来实现参数的绑定。
定义注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumParam {
String value() default "";
/**
* 赋值调用方法
* 如果为空,默认调用name()方法
* 该方法必须是一个不含参数的方法,否则将会调用失败
*
* @return
*/
String valueMethod() default "";
}
value() : value用于绑定请求参数和方法参数名一致时的对应关系。比如
user?statusNo=1
。方法的参数写法如下: getUser(@EnumParam(value="statusNo") int status)
或者getUser(@EnumParam() int statusNo)
valueMethod() : 赋值时调用枚举中的方法。
如果该属性不传值则默认调用枚举类默认提供的 “ valueOf()
” 方法。如果自定义一个方法,该方法必须是一个不含参数的方法,否则将会调用失败。比如上述示例枚举 StatusEnum的 getValue()
方法。
定义枚举参数解析器
核心代码:
// 1
public class EnumParamArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(EnumParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// 2
EnumParam annotation = parameter.getParameterAnnotation(EnumParam.class);
Object object = null;
if (annotation != null) {
String parameterName = annotation.value();
// 3
if (!StringUtils.hasText(parameterName)) {
parameterName = annotation.name();
}
if (!StringUtils.hasText(parameterName)) {
parameterName = parameter.getParameterName();
}
String value = webRequest.getParameter(parameterName);
if (StringUtils.hasText(value)) {
// 4
Class<?> objectType = parameter.getParameterType();
String method = Objects.equals(annotation.valueMethod(), "") ? "valueOf" : annotation.valueMethod();
Object[] enumConstants = objectType.getEnumConstants();
// 如果方法没了就 抛出异常
Method declaredMethod = objectType.getDeclaredMethod(method);
try {
for (Object enumConstant : enumConstants) {
// 5
Object invoke = method.equals("valueOf") ? declaredMethod.invoke(enumConstant, enumConstant.toString()) : declaredMethod.invoke(enumConstant);
if (invoke != null) {
if (Convert.toStr(invoke).equals(Convert.toStr(value))) {
object = enumConstant;
break;
}
}
}
} catch (Exception e) {
log.error("参数enum转换失败:{}.{}[{}]", parameter.getContainingClass().getName(), parameter.getMethod().getName(), parameterName);
object = null;
}
}
mavContainer.addAttribute(parameterName, object);
}
return object;
}
}
枚举参数解析器(EnumParamArgumentResolver)实现 spring mvc的扩展接口HandlerMethodArgumentResolver。 从参数中获取是否标记了 EnumParam 注解,如果是则进行解析。 处理 EnumParam.value() 的值并进行赋值给parameterName。 通过返回的方式拿到需要执行的方法和目标枚举类的值。 通过循环枚举的值然后比较。如果匹配则立即跳出循环并mavContainer.addAttribute(parameterName, object);然后返回。
以上就是实现枚举参数解析器的全部步骤。
示例
方法示例:
请求示例:
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud 视频教程:https://doc.iocoder.cn/video/
Part3 json字符串
我们有时候可能会遇到这种请求:
localhost:8088/prt/jsonParams?user={"age":12,"id":"1","name":"凹凸曼"}
那么这种我们可能一般都是使用String接收,然后在调用转JSON的API进行处理。可是这种代码每个方法都去写的话,太不优雅了。毕竟:「温柔永不落伍, 优雅永不过时 」 。
2 实现方式
通过定时JSON参数注解来标记参数:这是一个JSON字符串的参数。 通过自定义参数解析器来分析JSON字符串参数注解,来实现参数和对象属性的绑定。
定义注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam {
String value() default ""
Class<?> objectType() default JsonParam.class;
}
value() : value用于绑定请求参数和方法参数名一致时的对应关系。和 EnumParam中的value定义差不多。 objectType() : 当参数是数组对象时,赋值属性。
定义Json参数解析器
核心代码:
public class JsonParamArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation( JsonParam.class );
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
JsonParam annotation = parameter.getParameterAnnotation( JsonParam.class );
Object object = null;
if (annotation != null) {
String parameterName = annotation.value();
if (StringUtils.isBlank( parameterName )) {
parameterName = annotation.name();
}
if (StringUtils.isBlank( parameterName )) {
parameterName = parameter.getParameterName();
}
String value = webRequest.getParameter( parameterName );
if (StringUtils.isNotBlank( value )) {
// 2
Class<?> objectType = annotation.objectType();
try {
if (objectType != JsonParam.class) {
object = JSON.parseArray( value, objectType );
} else {
object = JSON.parseObject( value, parameter.getParameterType() );
}
} catch (Exception e) {
LoggerFactory.getLogger( JsonParamArgumentResolver.class )
.error( "参数Json转换失败:" + parameter.getContainingClass().getName() + "." + parameter.getMethod().getName() + "[" + parameterName + "]" );
object = null;
}
}
//this.validate( parameter, mavContainer, object, parameterName );
mavContainer.addAttribute( parameterName, object );
}
return object;
}
}
上述步骤的大部分逻辑和 枚举参数解析器 大体一致。 步骤2是判断objectType是否是JsonParam类型,如果是则是对象类型;如果不是JsonParam,这是数组对象类型。
以上就是实现Json参数解析器的全部步骤。
示例
示例1
普通对象方法示例:
请求示例:
示例2
数组对象方法示例:
请求示例:
Part4 SpringMvc自带解析器
3 普通参数绑定&@RequestParam
一般我们普通的参数我们无需加任何额外的注解标记,spring既可以给我们自定绑定参数。
这种,当我们的请求参数与方法参数不一致时可以使用@RequestParam
如下:
4 @PathVariable
在Controller方法的参数前面添加@PathVariable注解,将路径参数的值绑定到对应的参数上。
如下:
5 @RequestBody
在Controller方法的参数前面添加@RequestBody注解,将请求体的值绑定到对应的参数上 。注意这种模式不支持: Content-Type: application/x-www-form-urlencoded
或 Content-Type: application/x-www-form
的请求。
6 @ModelAttribute
在Controller方法的参数前面添加@ModelAttribute注解,将表单参数的值绑定到对应的参数上。同上这种模式不支持 Content-Type: application/json
的请求。
Part5 附录
7 问题1
springBoot+tomcat报错:_Invalid character found in the request target \[...\]. The valid characters are defined in RFC 7230 and RFC 3986_
原因是:SpringBoot 2.0.0 以上都采用内置tomcat8.0以上版本,而tomcat8.0以上版本遵从RFC规范添加了对Url的特殊字符的限制,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~
四个特殊字符以及保留字符( ! * ’ ( ) ; : @ & = + $ , / ? # \[ \] ) (26*2+10+4+18=84)
这84个字符,请求中出现了{}
大括号或者\[\]
,所以tomcat报错。
处理办法:在application.yml配置文件中如下配置:
server:
tomcat:
relaxed-path-chars: ['|','{','}','[',']']
relaxed-query-chars: ['|','{','}','[',']']
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
微信扫码关注该文公众号作者