如题,组件名为 “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 238 天前
下次一定用
|
2
HojiOShi 238 天前
我虽然不是搞后端这方向的,不过 java 一定有很多同类的库,你的这个和已有的库相比有什么优势吗?看到测试也没有写,怎么让人放心用到生产环境中去呢?
|
3
sticki OP @HojiOShi
1. 目前没有找到功能和我这个一样的库,它的优势就是我上面写到的 “解决了什么问题” 部分 2. 目前确实没有写测试用例,只有少数的使用示例在一个单独的项目中,这块确实需要补充,感谢提醒 |
4
fkdog 238 天前
就这句:
@SpelNotNull(condition = "#this.switchAudio == true", message = "语音内容不能为空") 我自己定义一个静态方法,ExceptionUtils.throwIf(this.switchAudio, "语音内容不能为空")不就好了? 为什么还要额外引入你一个类库呢,而且借助反射 API 还会降低额外性能。 而且参数校验逻辑是一个很个性化的东西,javax validation 自带的满足最通用的足矣。 |
5
watzds 238 天前
IDE 查看使用、重构之类不友好吧
|
6
firecooloo1024 238 天前 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 238 天前 via Android
@sticki 一般常用注解加字段上就够了,只有你说的枚举、字段联合、复杂校验等才单独写个 is 方法校验,校验逻辑集中,逻辑清晰,没有心智负担,就多了个自定义方法而已,太纯粹的贫血模型也不怎么好。你那个当做学习还行,生产不敢用,哈哈哈嗝
|
11
LeegoYih 238 天前
这么写有点恐怖
|
12
xwayway 237 天前
condition 和 assertTrue 里面调用属性、方法 全是字符串,对于重构很不友好。
|
13
justNoBody 237 天前
我觉得给出来的例子不是很好。
多字段联合校验中,`contentType=1`和`contentType=2`其实是两个不同的业务,如果用了您的`SpEL Validator`,这个业务校验逻辑就放到了`POJO`中。 我个人认为最好是放到业务实现中,以免产生不必要的耦合。 |
14
yihy8023 237 天前 1
给你点赞~感觉作为 javax.validation 额外的补充包不错。
个人觉得楼主直接用 condition 表达式灵活度太高了,里面的规则很难控制复杂度,把它作为保底手段,并且不要写复杂规则还行。condition 用成表达式会导致失去了 java 静态编译的检查,使错误从编译期便到了运行时,并且还要调用才能触发错误,危险程度太高了。假设你改了个字段名,对应的 condition 没改,上到生产后,客户一使用,才发现报错,你慌不慌。 最后提个建议 在启动时扫描注解,把注解的表达式都编译一遍缓存下来,至少问题能在启动时发现。 |
15
zmal 237 天前
点赞!
但 java 现在几乎是一个纯工业语言,灵活性相比 其他新兴语言差一些。在 java 里追求灵活性的各种魔法,反而会抛弃 java 相对严谨的语言特性。过于复杂的 SpEL 表达式不是一个很好的方案。 |
16
sticki OP @xwayway 这个问题我在 #9 回复过,实际上 idea 可以识别 SpEL 表达式,识别后字符串会有引用的效果,但目前我的组件对这个识别功能还不完全兼容
|
17
sticki OP |
18
chent114514 235 天前
那我要是来个 ipv6 规则校验呢
|
19
fengpan567 235 天前
手写 spel 。。。
|
20
sticki OP @chent114514 嘿,兄弟,注意我提的第三个示例,复杂逻辑校验,可以调用静态方法。你写一个 ipv6 的校验规则,然后在表达式里调用它就好了。
|
21
Leoking222 209 天前
给你点赞,兄弟
|