在Java项目开发中,Lombok通过注解简化了实体类的getter、setter、构造方法等模板代码的编写,但默认的Lombok注解无法支持“至少一个字段必须存在”这类自定义校验规则。这类需求在业务场景中非常常见,比如用户注册时,手机号、邮箱、第三方账号标识至少要提供一个,否则不允许提交数据。

基于JSR303自定义校验注解实现
JSR303是Java的校验规范,我们可以自定义一个校验注解,配合校验器实现至少一个字段非空的逻辑,这种方式可以和Spring的校验体系无缝集成,适合Web项目使用。
1. 定义自定义校验注解
首先创建@AtLeastOneNotNull注解,指定需要校验的字段组:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = AtLeastOneNotNullValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AtLeastOneNotNull {
// 需要校验的字段名称数组
String[] fieldNames();
// 校验失败后的提示信息
String message() default "至少一个字段不能为空";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2. 实现校验器逻辑
创建对应的校验器,通过反射获取指定字段的值,判断是否至少有一个不为空:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
public class AtLeastOneNotNullValidator implements ConstraintValidator<AtLeastOneNotNull, Object> {
private String[] fieldNames;
@Override
public void initialize(AtLeastOneNotNull constraintAnnotation) {
this.fieldNames = constraintAnnotation.fieldNames();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
for (String fieldName : fieldNames) {
Field field = value.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object fieldValue = field.get(value);
// 判断字段值不为空
if (fieldValue != null && !"".equals(fieldValue.toString().trim())) {
return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
}
3. 在Lombok类中使用注解
在Lombok标注的实体类上添加自定义注解,指定需要校验的字段:
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@AtLeastOneNotNull(fieldNames = {"phone", "email", "idCard"}, message = "手机号、邮箱、身份证号至少要提供一个")
public class UserRegisterDTO {
private String phone;
private String email;
private String idCard;
private String username;
}
在Spring接口中使用@Valid注解触发校验,校验失败会抛出MethodArgumentNotValidException异常,可统一捕获处理。
在Lombok构造方法中添加校验逻辑
如果项目没有引入校验框架,也可以利用Lombok的@AllArgsConstructor或者自定义构造方法,在构造对象时执行校验逻辑。
1. 使用@Builder配合自定义构造方法
Lombok的@Builder可以生成建造者模式代码,我们可以在建造者的build方法中添加校验:
import lombok.Builder;
import lombok.Getter;
@Getter
public class ContactInfo {
private final String phone;
private final String email;
private final String address;
@Builder
private ContactInfo(String phone, String email, String address) {
// 校验至少一个字段不为空
if (phone == null && email == null && address == null) {
throw new IllegalArgumentException("手机号、邮箱、地址至少要提供一个");
}
this.phone = phone;
this.email = email;
this.address = address;
}
// 自定义建造者,暴露静态方法创建建造者
public static ContactInfoBuilder builder() {
return new ContactInfoBuilder();
}
// 内部建造者类,重写build方法
public static class ContactInfoBuilder {
private String phone;
private String email;
private String address;
ContactInfoBuilder() {
}
public ContactInfoBuilder phone(String phone) {
this.phone = phone;
return this;
}
public ContactInfoBuilder email(String email) {
this.email = email;
return this;
}
public ContactInfoBuilder address(String address) {
this.address = address;
return this;
}
public ContactInfo build() {
return new ContactInfo(this.phone, this.email, this.address);
}
@Override
public String toString() {
return "ContactInfoBuilder{" +
"phone='" + phone + ''' +
", email='" + email + ''' +
", address='" + address + ''' +
'}';
}
}
}
使用示例:
public class Test {
public static void main(String[] args) {
// 正常创建对象
ContactInfo info1 = ContactInfo.builder()
.phone("13800138000")
.build();
// 校验失败会抛出异常
try {
ContactInfo info2 = ContactInfo.builder().build();
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 自定义JSR303注解 | 和Spring校验体系集成好,支持统一异常处理,注解复用性高 | 需要引入校验依赖,实现步骤稍多 | Spring Boot Web项目,需要统一参数校验的场景 |
| 构造方法/Builder校验 | 无额外依赖,逻辑直观,编译期就能发现问题 | 校验逻辑和类强耦合,复用性低,不支持自动校验触发 | 非Web项目,或者校验逻辑仅在该类使用的场景 |
注意事项
- 自定义JSR303校验器中使用反射获取字段时,要注意字段的访问权限,私有字段需要调用
setAccessible(true) - 如果字段是基础类型(如int、boolean),默认值不为null,需要额外判断默认值是否符合业务要求
- Lombok的
@Data注解会生成所有字段的getter和setter,如果校验逻辑放在setter中,要注意避免无限递归的问题
Lombok字段校验Java_Bean自定义注解validation修改时间:2026-07-20 15:48:31