别再死记硬背了!用Python复现同花顺VR、VMA等10个冷门技术指标(附完整代码)
2026/6/12 4:46:48
在 Spring Boot 开发中,我们习惯了使用@NotNull,@Size,@Pattern来校验参数。但是,业务往往比这复杂得多。
场景举例:
我们有一个用户保存接口(UserSaveReqVO),既用于新增,也用于修改。
id为空,但password必须填。id必填,但password可以为空(为空代表不修改密码)。痛点:
如果直接在password字段上加@NotBlank,那么修改接口也会报错。如果不加,新增接口就不安全。怎么解决?
@AssertTrue是 Bean Validation (Hibernate Validator) 提供的一个标准注解。
true,校验通过。false,校验失败,抛出异常并返回message中的错误信息。核心价值:它允许我们在 DTO 内部编写一段 Java 代码,来进行自定义的、跨字段的、带有业务逻辑的校验。
我们不需要定义两个 DTO,也不需要搞复杂的 Group,直接在 VO 内部写一个方法即可。
importcom.fasterxml.jackson.annotation.JsonIgnore;importio.swagger.v3.oas.annotations.media.Schema;importlombok.Data;importjavax.validation.constraints.AssertTrue;importjava.util.Objects;@Data@Schema(description="用户保存请求 VO")publicclassUserSaveReqVO{@Schema(description="用户ID (新增为空,修改必填)")privateLongid;@Schema(description="账号")privateStringusername;@Schema(description="密码")privateStringpassword;// =================================================// 核心黑科技:自定义逻辑校验// =================================================@AssertTrue(message="新增用户时,密码不能为空")@JsonIgnore// ⚠️ 坑点预警:必须加这个,否则会多出一个名为 passwordValid 的字段返回给前端publicbooleanisPasswordValid(){// 逻辑拆解:// 1. 如果 id 不为空,说明是“修改模式”。// 修改模式下对密码没要求,直接返回 true (通过)。if(id!=null){returntrue;}// 2. 如果 id 为空,说明是“新增模式”。// 新增模式下,密码必须有值。returnpassword!=null&&!password.trim().isEmpty();}}Controller 使用方式:
完全不用改,照常加@Valid即可。
@PostMapping("/save")publicCommonResult<Boolean>saveUser(@Valid@RequestBodyUserSaveReqVOreqVO){// 如果校验失败,这里进不来,全局异常处理器会处理returnsuccess(userService.saveUser(reqVO));}很多教程会推荐使用groups属性来解决这个问题,但在实战工程中,@AssertTrue 往往更胜一筹。
CreateGroup和UpdateGroup接口。然后字段上写@Null(groups=Create.class),@NotNull(groups=Update.class)… 代码看起来很乱。if-else清晰易读。@Validated(CreateGroup.class),校验就失效了,容易出 Bug。@Valid,DTO 自己会根据 ID 是否为空来判断该检查什么。这是 Group 做不到的。
type是 “手机注册”,则mobile必填;如果type是 “邮箱注册”,则email必填。if (type==1) return mobile != null;,轻松搞定。在使用@AssertTrue这种方法级校验时,有两个细节必须注意:
is或get开头:isValid()或isXxxValid()。@JsonIgnore:passwordValid: true)返回给前端,或者导致 Swagger 文档里多出莫名其妙的字段。@NotNull,@Size等标准注解。@AssertTrue+ 自定义方法。它让你的代码更内聚、更易读,并且避免了 Controller 层繁琐的分组配置。