Java中可通过自定义校验注解实现业务规则与校验逻辑解耦,核心是@Constraint注解声明+ConstraintValidator实现,支持字段级(如手机号格式)和类级(如日期范围)校验,配合Spring自动集成。

在 Java 中,通过自定义校验注解可以将业务规则与数据校验逻辑解耦,提升代码可读性和复用性。核心是结合 javax.validation(Jakarta Bean Validation)规范,实现注解声明 + 约束验证器(ConstraintValidator)的组合。
定义自定义校验注解
注解需标注 @Constraint,并指定对应的验证器类。例如,校验手机号是否符合国内格式:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
String message() default "手机号格式不正确";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
注意:
- message() 支持占位符(如 {javax.validation.constraints.NotBlank.message}),也可动态解析
- groups() 和 payload() 保持默认即可,用于分组校验和扩展元数据
编写对应的约束验证器
实现 ConstraintValidator<validphonenumber string></validphonenumber>,重写 isValid() 方法:
public class PhoneNumberValidator implements ConstraintValidator<validphonenumber string><pre class="brush:php;toolbar:false;">@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.trim().isEmpty()) {
return true; // 允许为空,由 @NotBlank 单独控制
}
// 简单正则:11位数字,以1开头
return value.matches("1[3-9]\d{9}");
}
}
说明:
- 验证器类必须有无参构造函数,Spring 会自动注册为 bean(若使用 Spring Boot)
- 返回 true 表示校验通过;false 触发错误,自动使用注解中 message() 提示
- 若需更灵活提示(如区分“为空”和“格式错”),可在 isValid 中调用 context.disableDefaultConstraintViolation() 自定义错误路径和消息
在实体类中使用注解
直接加在字段上,配合其他校验注解一起使用:
public class User {
@NotBlank(message = "姓名不能为空")
private String name;
<pre class="brush:php;toolbar:false;">@ValidPhoneNumber
private String phone;
@Min(value = 18, message = "年龄不能小于18")
private Integer age;}
Controller 层启用校验:
@PostMapping("/user")
public ResponseEntity<string> createUser(@Valid @RequestBody User user) {
// 业务逻辑
return ResponseEntity.ok("success");
}
</string>
Spring MVC 会自动触发级联校验,并将失败结果封装进 BindingResult(如需手动处理)。
支持多参数或复杂场景的进阶写法
若校验逻辑依赖多个字段(如“结束时间不能早于开始时间”),应使用类级别注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateRangeValidator.class)
public @interface ValidDateRange {
String start() default "startTime";
String end() default "endTime";
String message() default "结束时间不能早于开始时间";
// ...
}
验证器中通过反射获取字段值进行比较。此时 isValid() 的第一个参数是整个对象(Object),而非某个字段。
小提示:
- 注解属性名(如 start)需与实体字段名一致,或通过 SpEL 表达式增强灵活性(需自行解析)
- 避免在验证器中做耗时操作(如查库),校验应轻量、纯内存计算
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











