Java案例如何实现验证框架?

wen java案例 2

本文目录导读:

Java案例如何实现验证框架?

  1. 自定义注解验证框架
  2. 手动校验框架
  3. 使用示例
  4. 自定义验证器工厂

我来详细讲解Java中实现验证框架的几种常见方式,从简单到复杂。

自定义注解验证框架

1 定义自定义注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
@Documented
public @interface ValidPhone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    // 定义手机号格式
    String pattern() default "^1[3-9]\\d{9}$";
}

2 实现验证器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
    private String pattern;
    @Override
    public void initialize(ValidPhone constraintAnnotation) {
        this.pattern = constraintAnnotation.pattern();
    }
    @Override
    public boolean isValid(String phone, ConstraintValidatorContext context) {
        if (phone == null || phone.trim().isEmpty()) {
            return true; // 允许为空,如需必填可添加@NotEmpty
        }
        return phone.matches(pattern);
    }
}

3 实体类中使用

import javax.validation.constraints.*;
import java.time.LocalDate;
public class User {
    @NotNull(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度需要在2-20之间")
    private String username;
    @NotBlank(message = "密码不能为空")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,20}$", 
             message = "密码需包含大小写字母和数字,长度8-20")
    private String password;
    @Email(message = "邮箱格式不正确")
    private String email;
    @ValidPhone
    private String phone;
    @Min(value = 18, message = "年龄必须大于18岁")
    @Max(value = 100, message = "年龄必须小于100岁")
    private int age;
    @Past(message = "出生日期必须是过去的时间")
    private LocalDate birthDate;
    @Positive(message = "金额必须为正数")
    private double balance;
    // getters and setters
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    // ... 其他getter/setter
}

手动校验框架

1 创建校验器工具类

import java.lang.reflect.Field;
import java.util.*;
public class ValidationUtils {
    private static final Map<Class<?>, List<Field>> FIELD_CACHE = new HashMap<>();
    /**
     * 验证对象
     */
    public static ValidationResult validate(Object obj) {
        ValidationResult result = new ValidationResult();
        if (obj == null) {
            result.addError("对象不能为空");
            return result;
        }
        Class<?> clazz = obj.getClass();
        List<Field> fields = getFields(clazz);
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(obj);
                validateField(field, value, result);
            } catch (IllegalAccessException e) {
                result.addError(field.getName() + ": 访问异常");
            }
        }
        return result;
    }
    /**
     * 获取类中所有字段(包含父类)
     */
    private static List<Field> getFields(Class<?> clazz) {
        return FIELD_CACHE.computeIfAbsent(clazz, c -> {
            List<Field> fields = new ArrayList<>();
            Class<?> currentClass = c;
            while (currentClass != null) {
                fields.addAll(Arrays.asList(currentClass.getDeclaredFields()));
                currentClass = currentClass.getSuperclass();
            }
            return fields;
        });
    }
    /**
     * 验证单个字段
     */
    private static void validateField(Field field, Object value, ValidationResult result) {
        // 获取字段上的注解
        NotNull notNull = field.getAnnotation(NotNull.class);
        NotBlank notBlank = field.getAnnotation(NotBlank.class);
        Size size = field.getAnnotation(Size.class);
        Pattern pattern = field.getAnnotation(Pattern.class);
        Email email = field.getAnnotation(Email.class);
        Min min = field.getAnnotation(Min.class);
        Max max = field.getAnnotation(Max.class);
        // 校验NotNul
        if (notNull != null && value == null) {
            result.addError(field.getName() + ": " + notNull.message());
            return;
        }
        // 如果值为空,跳过后续校验
        if (value == null) {
            return;
        }
        // 校验NotBlank
        if (notBlank != null && value instanceof String) {
            if (((String) value).trim().isEmpty()) {
                result.addError(field.getName() + ": " + notBlank.message());
            }
        }
        // 校验Size
        if (size != null && value instanceof String) {
            int length = ((String) value).length();
            if (length < size.min() || length > size.max()) {
                result.addError(field.getName() + ": " + size.message());
            }
        }
        // 校验Pattern
        if (pattern != null && value instanceof String) {
            if (!((String) value).matches(pattern.regexp())) {
                result.addError(field.getName() + ": " + pattern.message());
            }
        }
        // 校验Email
        if (email != null && value instanceof String) {
            String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
            if (!((String) value).matches(emailRegex)) {
                result.addError(field.getName() + ": " + email.message());
            }
        }
        // 校验Min/Max
        if (value instanceof Number) {
            double numValue = ((Number) value).doubleValue();
            if (min != null && numValue < min.value()) {
                result.addError(field.getName() + ": " + min.message());
            }
            if (max != null && numValue > max.value()) {
                result.addError(field.getName() + ": " + max.message());
            }
        }
    }
}

2 验证结果类

public class ValidationResult {
    private boolean hasErrors = false;
    private List<String> errors = new ArrayList<>();
    private Map<String, String> fieldErrors = new HashMap<>();
    public void addError(String error) {
        this.hasErrors = true;
        this.errors.add(error);
    }
    public void addFieldError(String field, String error) {
        this.hasErrors = true;
        this.fieldErrors.put(field, error);
        this.errors.add(field + ": " + error);
    }
    public boolean hasErrors() {
        return hasErrors;
    }
    public List<String> getErrors() {
        return errors;
    }
    public Map<String, String> getFieldErrors() {
        return fieldErrors;
    }
    public String getFirstError() {
        return errors.isEmpty() ? null : errors.get(0);
    }
    @Override
    public String toString() {
        return String.join("; ", errors);
    }
}

使用示例

1 在Spring Boot中使用

import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/users")
public class UserController {
    @PostMapping("/register")
    public Result register(@Valid @RequestBody User user) {
        // 如果验证失败,Spring会自动返回400错误
        return Result.success("注册成功");
    }
    @PostMapping("/manual")
    public Result manualRegister(@RequestBody User user) {
        ValidationResult result = ValidationUtils.validate(user);
        if (result.hasErrors()) {
            return Result.error(result.getFirstError());
        }
        return Result.success("注册成功");
    }
}

2 分组验证

// 定义分组接口
public interface CreateGroup {}
public interface UpdateGroup {}
// 实体类中使用
public class User {
    @NotNull(groups = CreateGroup.class, message = "创建用户时用户名不能为空")
    @NotNull(groups = UpdateGroup.class, message = "更新用户时用户名不能为空")
    private String username;
    @Null(groups = UpdateGroup.class, message = "更新时不能修改ID")
    @NotNull(groups = CreateGroup.class, message = "创建时必须指定ID")
    private Long id;
}
// 使用分组
@PostMapping("/create")
public Result createUser(@Validated(CreateGroup.class) @RequestBody User user) {
    // ...
}
@PutMapping("/update")
public Result updateUser(@Validated(UpdateGroup.class) @RequestBody User user) {
    // ...
}

3 复杂对象嵌套验证

public class Order {
    @NotNull(message = "订单ID不能为空")
    private Long orderId;
    @Valid  // 嵌套验证
    @NotNull(message = "用户信息不能为空")
    private User user;
    @Valid
    @NotEmpty(message = "订单项不能为空")
    private List<OrderItem> items;
}
public class OrderItem {
    @NotNull(message = "商品ID不能为空")
    private Long productId;
    @Min(value = 1, message = "数量必须大于0")
    private Integer quantity;
    @Positive(message = "价格必须为正数")
    private BigDecimal price;
}

自定义验证器工厂

public class ValidatorFactory {
    private static Map<String, Validator> validatorMap = new HashMap<>();
    static {
        validatorMap.put("phone", new PhoneValidator());
        validatorMap.put("idCard", new IdCardValidator());
        validatorMap.put("bankCard", new BankCardValidator());
    }
    public static Validator getValidator(String type) {
        Validator validator = validatorMap.get(type);
        if (validator == null) {
            throw new IllegalArgumentException("不支持的验证类型: " + type);
        }
        return validator;
    }
    // 动态注册验证器
    public static void registerValidator(String type, Validator validator) {
        validatorMap.put(type, validator);
    }
}
// 验证器接口
public interface Validator {
    boolean validate(Object value);
    String getErrorMessage();
}
// 身份证验证器示例
public class IdCardValidator implements Validator {
    @Override
    public boolean validate(Object value) {
        if (value == null || !(value instanceof String)) {
            return false;
        }
        String idCard = (String) value;
        // 简单的身份证验证逻辑
        return idCard.matches("^\\d{17}[0-9Xx]$");
    }
    @Override
    public String getErrorMessage() {
        return "身份证号码格式不正确";
    }
}

实现验证框架的核心要点:

  1. 注解驱动:利用Java注解简化验证逻辑
  2. 反射机制:动态获取字段和注解信息
  3. 策略模式:不同的验证规则使用不同的验证器
  4. 可扩展性:支持自定义注解和验证器
  5. 分组验证:不同场景使用不同的验证规则
  6. 嵌套验证:支持复杂对象的层级验证

选择合适的验证方式取决于项目规模和需求:

  • 简单项目:手动校验或Hibernate Validator
  • 复杂项目:Spring Boot + Bean Validation + 自定义注解
  • 极致性能:自定义反射缓存 + 预编译正则

抱歉,评论功能暂时关闭!