如题,组件名为 “SpEL Validator”,下面我会进行一些介绍,希望各位看完后可以发表一些看法。
「 SpEL Validator 」是基于 SpEL 的参数校验包,也是 javax.validation 的扩展增强包,用于简化参数校验。
枚举值字段校验:
@SpelAssert(assertTrue = " T(cn.sticki.enums.UserStatusEnum).getByCode(#this.userStatus) != null ", message = "用户状态不合法") private Integer userStatus;
多字段联合校验:
@NotNull private Integer contentType; @SpelNotNull(cOndition= "#this.cOntentType== 1", message = "语音内容不能为空") private Object audioContent; @SpelNotNull(cOndition= "#this.cOntentType== 2", message = "视频内容不能为空") private Object videoContent;
复杂逻辑校验,调用静态方法:
// 中文算两个字符,英文算一个字符,要求总长度不超过 10 // 调用外部静态方法进行校验 @SpelAssert(assertTrue = "T(cn.sticki.util.StringUtil).getLength(#this.userName) <= 10", message = "用户名长度不能超过 10") private String userName;
调用 Spring Bean (需要使用 @EnableSpelValidatorBeanRegistrar 开启 Spring Bean 支持):
// 这里只是简单举例,实际开发中不建议这样判断用户是否存在 @SpelAssert(assertTrue = "@userService.getById(#this.userId) != null", message = "用户不存在") private Long userId;
等待探索……
添加依赖
<dependency> <groupId>cn.sticki</groupId> <artifactId>spel-validator</artifactId> <version>Latest Version</version> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>${hibernate-validator.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot-starter-web.version}</version> </dependency>
在接口参数上使用 @Valid
或 @Validated
注解
@RestController @RequestMapping("/example") public class ExampleController { /** * 简单校验示例 */ @PostMapping("/simple") public Resp<Void> simple(@RequestBody @Valid SimpleExampleParamVo simpleExampleParamVo) { return Resp.ok(null); } }
在实体类上使用 @SpelValid
注解,同时在需要校验的字段上使用 @SpelNotNull
等约束注解
@Data @SpelValid public class SimpleExampleParamVo { @NotNull private Boolean switchAudio; /** * 当 switchAudio 为 true 时,校验 audioContent ,audioContent 不能为 null */ @SpelNotNull(cOndition= "#this.switchAudio == true", message = "语音内容不能为空") private Object audioContent; }
发起请求,即可看到校验结果
性能上我目前还没有进行测试,但代码里使用了很多的反射,会有一定的损耗,后面我准备多加一些缓存,尽量降低性能上的影响。
以上是关于这个组件的大概介绍,希望各位大佬能够对此发表一些看法,好或者不好都可以发表,感谢各位~
感兴趣的朋友也可以到 GitHub 或者掘金查看详细情况。
GitHub 地址: https://github.com/stick-i/spel-validator
我在掘金发布的详细说明文章: https://juejin.cn/post/7365698962531401766
1 jwj 2024-05-06 17:57:08 +08:00 下次一定用 |
![]() | 2 HojiOShi 2024-05-06 19:14:05 +08:00 我虽然不是搞后端这方向的,不过 java 一定有很多同类的库,你的这个和已有的库相比有什么优势吗?看到测试也没有写,怎么让人放心用到生产环境中去呢? |
![]() | 3 sticki OP @HojiOShi 1. 目前没有找到功能和我这个一样的库,它的优势就是我上面写到的 “解决了什么问题” 部分 2. 目前确实没有写测试用例,只有少数的使用示例在一个单独的项目中,这块确实需要补充,感谢提醒 |
4 fkdog 2024-05-06 19:54:52 +08:00 就这句: @SpelNotNull(cOndition= "#this.switchAudio == true", message = "语音内容不能为空") 我自己定义一个静态方法,ExceptionUtils.throwIf(this.switchAudio, "语音内容不能为空")不就好了? 为什么还要额外引入你一个类库呢,而且借助反射 API 还会降低额外性能。 而且参数校验逻辑是一个很个性化的东西,javax validation 自带的满足最通用的足矣。 |
![]() | 5 watzds 2024-05-06 20:12:37 +08:00 IDE 查看使用、重构之类不友好吧 |
6 firecooloo1024 2024-05-06 20:14:33 +08:00 via Android 其实没必要,记这么多规则增加负担。试试这样写: ```java @Data public class UserVo { private String username; private Integer age; private List<String> hobby; @AssertTrue public boolean isValid() { return StringUtils.isNotEmpty(username) && age > 0 && age < 100 && !hobby.isEmpty(); } } ``` |
![]() | 7 sticki OP ![]() @fkdog 当然可以自己写代码实现,如果愿意的话,javax validation 也可以不用。4G 普及之前,大家也觉得没必要,我认为这是一样的道理。 至于反射降低的性能,对于一个接口请求来说,只是九牛一毛罢了,框架带来的便利性,往往都会牺牲一定的性能,那几毫秒的延迟,在绝大多数场景下,都是不重要的。举个不恰当的例子,Spring 内也包含了大量的反射,但没人在乎。 再说说个性化,这套组件就是为了解决个性化的参数校验而生的,它几乎可以满足任何个性化的参数校验。 |
![]() | 8 sticki OP @firecooloo1024 我也这样写过,没什么毛病,就是代码略多一点。这套组件的规则并不复杂,其实和 javax validation 那些注解差不了多少,唯一需要学习的是 SpEL 的语法,但其实也很简单。 |
![]() | 9 sticki OP ![]() |
10 firecooloo1024 2024-05-06 21:07:19 +08:00 via Android @sticki 一般常用注解加字段上就够了,只有你说的枚举、字段联合、复杂校验等才单独写个 is 方法校验,校验逻辑集中,逻辑清晰,没有心智负担,就多了个自定义方法而已,太纯粹的贫血模型也不怎么好。你那个当做学习还行,生产不敢用,哈哈哈嗝 |
![]() | 11 LeegoYih 2024-05-06 21:13:32 +08:00 这么写有点恐怖 |
12 xwayway 2024-05-07 08:42:16 +08:00 condition 和 assertTrue 里面调用属性、方法 全是字符串,对于重构很不友好。 |
13 justNoBody 2024-05-07 09:30:32 +08:00 我觉得给出来的例子不是很好。 多字段联合校验中,`cOntentType=1`和`cOntentType=2`其实是两个不同的业务,如果用了您的`SpEL Validator`,这个业务校验逻辑就放到了`POJO`中。 我个人认为最好是放到业务实现中,以免产生不必要的耦合。 |
![]() | 14 yihy8023 2024-05-07 09:37:46 +08:00 ![]() 给你点赞~感觉作为 javax.validation 额外的补充包不错。 个人觉得楼主直接用 condition 表达式灵活度太高了,里面的规则很难控制复杂度,把它作为保底手段,并且不要写复杂规则还行。condition 用成表达式会导致失去了 java 静态编译的检查,使错误从编译期便到了运行时,并且还要调用才能触发错误,危险程度太高了。假设你改了个字段名,对应的 condition 没改,上到生产后,客户一使用,才发现报错,你慌不慌。 最后提个建议 在启动时扫描注解,把注解的表达式都编译一遍缓存下来,至少问题能在启动时发现。 |
15 zmal 2024-05-07 10:09:32 +08:00 点赞! 但 java 现在几乎是一个纯工业语言,灵活性相比 其他新兴语言差一些。在 java 里追求灵活性的各种魔法,反而会抛弃 java 相对严谨的语言特性。过于复杂的 SpEL 表达式不是一个很好的方案。 |
![]() | 16 sticki OP @xwayway 这个问题我在 #9 回复过,实际上 idea 可以识别 SpEL 表达式,识别后字符串会有引用的效果,但目前我的组件对这个识别功能还不完全兼容 |
![]() | 17 sticki OP |
18 chent114514 2024-05-09 16:10:16 +08:00 那我要是来个 ipv6 规则校验呢 |
19 fengpan567 2024-05-09 17:37:47 +08:00 手写 spel 。。。 |
![]() | 20 sticki OP @chent114514 嘿,兄弟,注意我提的第三个示例,复杂逻辑校验,可以调用静态方法。你写一个 ipv6 的校验规则,然后在表达式里调用它就好了。 |
![]() | 21 Leoking222 2024-06-04 09:46:36 +08:00 给你点赞,兄弟 |