在企业级应用开发中,参数校验是保证数据质量的第一道防线。本文将深入介绍Java的Validator框架的使用,特别是如何结合@AssertTrue
注解实现复杂的业务校验逻辑。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
常用的校验注解包括:
@NotBlank
: 字符串不能为null且去除空格后长度必须大于0@Pattern
: 通过正则表达式校验字符串格式@Size
: 校验字符串、集合、数组等的长度范围@AssertTrue
: 校验方法返回值必须为true@NotNull
: 不能为null@Min
: 数值必须大于等于指定值@Max
: 数值必须小于等于指定值@Email
: 校验邮箱格式@Data
public class ImageRequest {
/**
* 图片URL地址
*/
@Pattern(regexp = "^(http|https)://.*", message = "图片URL格式不正确")
@Size(max = 1024, message = "图片URL长度不能超过1024个字符")
private String imageUrl;
/**
* Base64编码的图片数据
*/
@Size(max = 10 * 1024 * 1024, message = "Base64图片数据不能超过10MB")
private String base64Image;
/**
* 设备编号
*/
@NotBlank(message = "设备编号不能为空")
private String deviceNo;
/**
* 用户标识
*/
@NotBlank(message = "用户标识不能为空")
private String userId;
/**
* 自定义校验方法:确保至少提供一种图片数据
*/
@AssertTrue(message = "必须提供图片URL或Base64编码图片数据其中之一")
public boolean isImageDataValid() {
return StringUtils.isNotBlank(imageUrl) || StringUtils.isNotBlank(base64Image);
}
}
public class ValidationExample {
private final Validator validator;
public ValidationExample() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
}
public void validateRequest(ImageRequest request) {
// 执行校验
Set<ConstraintViolation<ImageRequest>> violations = validator.validate(request);
// 如果存在校验错误
if (!violations.isEmpty()) {
// 收集所有验证错误信息
String errorMessage = violations.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "));
log.error("参数校验失败:{}", errorMessage);
throw new IllegalArgumentException("参数校验失败:" + errorMessage);
}
}
}
@AssertTrue
注解用于复杂的业务校验场景,特别是涉及多个字段之间的关联校验时。
@AssertTrue(message = "校验失败的提示信息")
public boolean isXxxValid() {
// 校验逻辑
return true/false;
}
@AssertTrue(message = "付款方式只能选择其中一种")
public boolean isPaymentMethodValid() {
return (alipay != null) ^ (wechatPay != null);
}
@AssertTrue(message = "当选择快递配送时,收货地址不能为空")
public boolean isDeliveryAddressValid() {
return !DeliveryType.EXPRESS.equals(deliveryType) ||
StringUtils.isNotBlank(deliveryAddress);
}
@AssertTrue(message = "结束时间必须晚于开始时间")
public boolean isTimeRangeValid() {
return endTime != null && startTime != null &&
endTime.after(startTime);
}
public interface Create {}
public interface Update {}
@AssertTrue(message = "创建时的校验规则", groups = {Create.class})
public boolean isCreateValid() {
return ...;
}
@AssertTrue(message = "更新时的校验规则", groups = {Update.class})
public boolean isUpdateValid() {
return ...;
}
@AssertTrue(message = "图片数据格式校验失败")
public boolean isImageFormatValid() {
if (StringUtils.isNotBlank(imageUrl)) {
return imageUrl.startsWith("http") &&
(imageUrl.endsWith(".jpg") || imageUrl.endsWith(".png"));
}
if (StringUtils.isNotBlank(base64Image)) {
return base64Image.startsWith("data:image/");
}
return false;
}
统一异常处理
创建全局异常处理器
统一校验失败的返回格式
性能优化
ValidatorFactory应该是单例的
避免在校验方法中进行重量级操作
代码规范
校验方法命名要规范且具有描述性
保持校验逻辑的简单清晰
测试覆盖
编写完整的单元测试
覆盖各种边界条件
文档维护
记录校验规则的业务含义
及时更新文档