news 2026/3/14 4:07:56

写了一套几乎无敌的参数校验组件!!!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
写了一套几乎无敌的参数校验组件!!!

参数校验这个东西,很多情况下都是比较简单的,用@NotNull@Size等注解就可以解决绝大多数场景,但也有一些场景是这些基本注解解决不了的,只能用一些其他的方式处理,这样就导致参数校验变成了多层,其实是不利于代码维护的。

于是乎,我写了一套几乎可以满足任何场景的参数校验组件,非常好用,安利给大家。

GitHub地址文末获取!

💡 它解决了什么问题?

  • 枚举值字段校验:

    @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;
  • 更多使用场景,欢迎探索和补充!

📝 特点

  • 强大的参数校验功能,几乎支持所有场景下的参数校验。

  • 扩展自 javax.validation 包,只新增不修改,无缝集成到项目中。

  • 基于 SpEL(Spring Expression Language) 表达式,支持复杂的校验逻辑。

  • 支持调用 Spring Bean,可在表达式中使用注入过的 Spring Bean。

  • 校验时基于整个对象,支持对象内字段间的校验逻辑。

  • 支持自定义校验注解,可根据业务需求自定义校验逻辑。

  • 无需额外的异常处理,校验失败时会上报到 javax.validation 的异常体系中。

  • 简单易用,使用方式几乎与 javax.validation 一致,学习成本低,上手快。

🎈 环境

目前仅测试了 JDK8 环境,理论上来说 JDK8+ 应该都是支持的。

📦 快速开始

  • 添加依赖

    Latest Version: 0.0.2-beta

    <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; }
  • 添加全局异常处理器,处理校验异常

    @RestControllerAdvice public class ControllerExceptionAdvice { @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class}) public Resp<Void> handleBindException(BindException ex) { String msg = ex.getFieldErrors().stream() .map(error -> error.getField() + " " + error.getDefaultMessage()) .reduce((s1, s2) -> s1 + "," + s2) .orElse(""); return new Resp<>(400, msg); } }
  • 发起请求,即可看到校验结果

    • 示例一:@SpelNotNull 校验不通过

      请求体:

      { "switchAudio": true, "audioContent": null }

      响应体

      { "code": 400, "message": "audioContent 语音内容不能为空", "data": null }
    • 示例二:校验通过

      请求体

      { "switchAudio": false, "audioContent": null }

      响应体

      { "code": 200, "message": "成功", "data": null }
    • 示例三:@NotNull 校验不通过

      请求体

      { "switchAudio": null, "audioContent": null }

      响应体

      { "code": 400, "message": "switchAudio 不能为null", "data": null }

📖 使用指南

注意:本组件的目的不是代替javax.validation的校验注解,而是作为一个扩展,方便某些场景下的参数校验。能够使用javax.validation的场景就不要使用spel-validator,因为spel-validator会有一定的性能损耗。

开启约束校验

需要满足以下两个条件,才会对带注解的元素进行校验:

  1. 在接口参数上使用@Valid@Validated注解

  2. 在实体类上使用@SpelValid注解

如果只满足第一个条件,那么只会对带@NotNull@NotEmpty@NotBlank等注解的元素进行校验。关注公众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!

如果只满足第二个条件,那么不会对任何元素进行校验。

这是因为@SpelValid注解是基于javax.validation.Constraint实现的,只有在@Valid@Validated注解的支持下才会生效。而spel-validator提供的约束注解是基于@SpelValid进行扫描校验的,只有在@SpelValid注解生效的情况下才会执行约束校验。

使用约束注解

目前支持的约束注解有:

注解

说明

对标 javax.validation

@SpelAssert

逻辑断言校验

@SpelNotNull

非 null 校验

@NotNull
@SpelNotEmpty

集合、字符串、数组大小非空校验

@NotEmpty
@SpelNotBlank

字符串非空串校验

@NotBlank
@SpelNull

必须为 null 校验

@Null
@SpelSize

集合、字符串、数组长度校验

@Size

每个约束注解都包含三个默认的属性:

  • message:校验失败时的提示信息。

  • group:分组条件,支持 SpEL 表达式,当分组条件满足时,才会对带注解的元素进行校验。

  • condition:约束开启条件,支持 SpEL 表达式,当 表达式为空 或 计算结果为true 时,才会对带注解的元素进行校验。

调用 Spring Bean

默认情况下,解析器无法识别 SpEL 表达式中的 Spring Bean。

如果需要在 SpEL 表达式中调用 Spring Bean,需要在启动类上添加@EnableSpelValidatorBeanRegistrar注解, 开启 Spring Bean 支持。

@EnableSpelValidatorBeanRegistrar @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

自定义约束注解

  1. 在注解上使用@SpelConstraint,并指定验证器。

  2. 然后给注解添加上三个固定的字段 message 、group、condition。

  3. 实现验证器

自定义示例约束示例-定义约束注解

自定义约束示例-实现验证器

具体实现方式可以参考cn.sticki.validator.spel.SpelConstraint类。关注公众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!

如果你使用过javax.validation的自定义约束注解,那么你会发现SpEL Validator的自定义约束注解几乎与javax.validation一致。

📦 示例项目

我也写了一个简单的示例项目,但目前还不太完善,只有基本的使用方法:

  • https://github.com/stick-i/spel-validator-example

最后

关于性能

性能上我目前还没有进行测试,但代码里使用了很多的反射,会有一定的损耗,后面我准备多加一些缓存,尽量降低性能上的影响。

一个奇怪的现象

在项目中使用SpEL的时候,有一个很怪异的现象:

我给这两个字段都标记了@Language("SpEL"),但是只有condition可以识别,我不知道这算不算idea的bug,我目前使用的idea版本是 IntelliJ IDEA 2024.1 (Ultimate Edition),有懂的朋友请帮忙解答一下。

GitHub地址:https://github.com/stick-i/spel-validator

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/13 22:06:49

从此告别拖延,AI论文写作软件千笔·专业论文写作工具 VS 万方智搜AI

随着人工智能技术的迅猛发展&#xff0c;AI辅助写作工具已逐渐成为高校学生完成毕业论文的重要帮手。越来越多的专科生开始借助这些智能工具来提升写作效率、优化论文结构&#xff0c;甚至在文献检索与格式规范方面也获得专业支持。然而&#xff0c;面对市场上种类繁多、功能各…

作者头像 李华
网站建设 2026/3/13 10:37:20

MAC物理地址和IP网络地址有什么区别?

目录 一、什么是MAC地址二、什么是IP地址三、如何隐藏真实的MAC地址四、如何隐藏真实的IP地址 一、什么是MAC地址 MAC地址&#xff0c;全称为媒体访问控制地址&#xff08;Media Access Control Address&#xff09;&#xff0c;是一种用于网络通信的唯一标识符。它是由IEEE 8…

作者头像 李华
网站建设 2026/3/14 3:23:27

Embedding模型深度解析:从词向量到语义空间的完整指南

本文深入剖析Embedding(嵌入)模型的核心原理,从最基础的词向量概念出发,详细讲解向量空间中的语义关系、相似度计算、训练方法,以及在搜索、推荐、RAG等场景中的实际应用。 一、什么是Embedding? 1.1 从One-Hot到Embedding 问题:计算机如何理解"猫"和"…

作者头像 李华
网站建设 2026/3/14 11:17:07

Substance P (2-11) (Deca-Substance P) ;PKPQPFFGLM-NH₂

一、基础信息 英文名称&#xff1a;Substance P (2-11) (Deca-Substance P)三字母序列&#xff1a;Pro-Lys-Pro-Gln-Gln-Phe-Phe-Gly-Leu-Met-NH₂单字母序列&#xff1a;PKPQPFFGLM-NH₂精确分子量&#xff1a;1191.46 Da等电点&#xff08;pI&#xff09;&#xff1a;6.0~6.…

作者头像 李华
网站建设 2026/3/14 10:51:31

48 多源动态最优潮流分布式鲁棒优化:应对风光不确定性

48多源动态最优潮流分布式鲁棒优化 关键词&#xff1a;分布式鲁棒优化 风光不确定性 最优潮流 Wasserstein距离 仿真软件&#xff1a;matlabyalmipcplex 参考文档&#xff1a;《多源动态最优潮流的分布鲁棒优化方法》 主要内容&#xff1a;针对大规模清洁能源接入电网引起的系统…

作者头像 李华
网站建设 2026/3/13 10:22:51

空指针之痛:除了 if!=null,你还有更优雅的办法吗?

一、 序言&#xff1a;那个价值十亿美元的错误 在 Java 世界里&#xff0c;java.lang.NullPointerException&#xff08;NPE&#xff09;是每个开发者的宿命。它的发明者 Tony Hoare 曾公开道歉&#xff0c;称其为“十亿美元的错误”。 在生产环境中&#xff0c;NPE 往往意味着…

作者头像 李华