本文目录导读:

我来详细讲解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 "身份证号码格式不正确";
}
}
实现验证框架的核心要点:
- 注解驱动:利用Java注解简化验证逻辑
- 反射机制:动态获取字段和注解信息
- 策略模式:不同的验证规则使用不同的验证器
- 可扩展性:支持自定义注解和验证器
- 分组验证:不同场景使用不同的验证规则
- 嵌套验证:支持复杂对象的层级验证
选择合适的验证方式取决于项目规模和需求:
- 简单项目:手动校验或Hibernate Validator
- 复杂项目:Spring Boot + Bean Validation + 自定义注解
- 极致性能:自定义反射缓存 + 预编译正则