本文目录导读:

我来详细讲解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 + 自定义注解
- 极致性能:自定义反射缓存 + 预编译正则
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播