Spring Validation实现多字段依赖校验

1,321 阅读1分钟

上篇完美实现校验:利用Spring Validation实现强大的输入验证我们通过Spring Validation实现参数的校验,今天我们看下多字段联合校验。

当根据一个字段的值来决定另一个字段的校验时,我们可以使用ConstraintValidator来实现自定义的校验逻辑。

在下面的示例中,我们假设根据登录类型来决定手机号码的校验逻辑。 首先,我们定义一个自定义的校验注解 PhoneValidation

import javax.validation.Constraint;  
import javax.validation.Payload;  
import java.lang.annotation.*;  
  
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Constraint(validatedBy = PhoneValidator.class)  
@Documented  
public @interface PhoneValidation {  
    String message() default "Invalid phone number";  
  
    Class<?>[] groups() default {};  
  
    Class<? extends Payload>[] payload() default {};  
  
    String loginTypeField();  
  
    String phoneField();  
}

接着,我们实现对应的验证器 PhoneValidator

import javax.validation.ConstraintValidator;  
import javax.validation.ConstraintValidatorContext;  
import java.lang.reflect.Field;  
  
public class PhoneValidator implements ConstraintValidator<PhoneValidation, Object> {  
    private String loginTypeField;  
    private String phoneField;  
  
    @Override  
    public void initialize(PhoneValidation constraintAnnotation) {  
        loginTypeField = constraintAnnotation.loginTypeField();  
        phoneField = constraintAnnotation.phoneField();  
    }  
  
    @Override  
    public boolean isValid(Object value, ConstraintValidatorContext context) {  
        try {  
            Field userType = value.getClass().getDeclaredField(loginTypeField);  
            userType.setAccessible(true);  
            String userTypeValue = (String) userType.get(value);  
  
            Field phoneNumber = value.getClass().getDeclaredField(phoneField);  
            phoneNumber.setAccessible(true);  
            String phoneNumberValue = (String) phoneNumber.get(value);  
  
            if ("1".equals(userTypeValue)) {  
                return phoneNumberValue != null && !phoneNumberValue.isEmpty();  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return true;  
    }  
  
}

在这个示例中,我们设置登录类型为 "1" 时,手机号码不能为空。isValid 方法中使用了反射来获取字段值并进行校验。

最后,我们可以在实体类上应用自定义的校验注解:

import lombok.Data;  
  
@Data  
@PhoneValidation(loginTypeField = "loginType", phoneField = "phoneNumber")  
public class User {  
  
    private String loginType;  
  
    private String phoneNumber;  
  
}

现在,当你使用 Bean Validation 来验证 User 对象时,会根据用户类型来决定手机号码的校验逻辑:

public class Main {  
    public static void main(String[] args) {  
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();  
        Validator validator = validatorFactory.getValidator();  

        User user = new User();  
        user.setLoginType("1");  
        user.setPhoneNumber(null);  

        Set<ConstraintViolation<User>> violations = validator.validate(user);  

        for (ConstraintViolation<User> violation : violations) {  
            System.out.println(violation.getMessage());  
        }  
    }  
}

当我们传入空手机号时输出错误信息

Invalid phone number