1. 概述
本文将深入探讨 Apache BVal 库对 Java Bean Validation 规范(JSR 349) 的实现。作为 Java 开发者,掌握 Bean Validation 能有效提升数据校验的健壮性,而 BVal 提供了一套轻量级且功能完备的解决方案。
2. Maven 依赖
要在项目中使用 Apache BVal,首先在 pom.xml
中添加以下核心依赖:
<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-jsr</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
若需使用 BVal 提供的扩展约束(如信用卡校验、IBAN 校验等),需额外引入可选依赖:
<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-extras</artifactId>
<version>1.1.2</version>
</dependency>
✅ 最新版本可通过 Maven Central 获取:
3. 应用约束
BVal 实现了 javax.validation
包中的所有标准约束。直接在属性声明上添加注解 即可应用约束:
public class User {
@NotNull
private String email;
private String password;
@Size(min=1, max=20)
private String name;
@Min(18)
private int age;
// 标准构造器、getter/setter
}
⚠️ 注意:@NotNull
确保字段非空,@Size
限制字符串长度,@Min
设置数值最小值——这些都是 Bean Validation 的基础操作,老驱动应该秒懂。
4. 校验 Bean
4.1. 获取 ValidatorFactory
强烈建议全局复用 ValidatorFactory
实例,因为其创建过程开销较大:
ValidatorFactory validatorFactory
= Validation.byProvider(ApacheValidationProvider.class)
.configure().buildValidatorFactory();
4.2. 获取 Validator
从工厂中获取 Validator
实例:
Validator validator = validatorFactory.getValidator();
✅ 线程安全设计:Validator
实例可安全复用,无需重复创建。
Validator
提供三个核心校验方法:
validate()
:校验整个对象validateProperty()
:校验单个属性validateValue()
:校验属性值(未绑定到对象)
4.3. validate() API
全量校验对象的所有约束,返回 ConstraintViolation
集合:
@Test
public void givenUser_whenValidate_thenValidationViolations() {
User user
= new User("user@example.com", "pass", "nameTooLong_______________", 15);
Set<ConstraintViolation<User>> violations = validator.validate(user);
assertTrue("no violations", violations.size() > 0);
}
4.4. validateProperty() API
精准校验单个属性,避免全量校验的性能开销:
@Test
public void givenInvalidAge_whenValidateProperty_thenConstraintViolation() {
User user = new User("user@example.com", "pass", "Ana", 12);
Set<ConstraintViolation<User>> propertyViolations
= validator.validateProperty(user, "age");
assertEquals("size is not 1", 1, propertyViolations.size());
}
4.5. validateValue() API
预校验属性值,在赋值前检查合法性:
@Test
public void givenValidAge_whenValidateValue_thenNoConstraintViolation() {
User user = new User("user@example.com", "pass", "Ana", 18);
Set<ConstraintViolation<User>> valueViolations
= validator.validateValue(User.class, "age", 20);
assertEquals("size is not 0", 0, valueViolations.size());
}
4.6. 关闭 ValidatorFactory
使用完毕后必须关闭工厂,避免资源泄漏:
if (validatorFactory != null) {
validatorFactory.close();
}
5. 非 JSR 约束
BVal 提供了 JSR 规范之外的扩展约束,增强校验能力:
核心扩展约束(bval-jsr
)
@Email
:校验邮箱格式@NotEmpty
:确保非空(区别于@NotNull
)
高级扩展约束(bval-extras
)
约束类型 | 示例注解 | 用途 |
---|---|---|
数字格式 | @IBAN |
国际银行账号校验 |
@Isbn |
标准书号校验 | |
@EAN13 |
国际商品编码校验 | |
信用卡校验 | @Visa |
Visa 卡号校验 |
@Mastercard |
Mastercard 卡号校验 | |
网络地址 | @Domain |
域名校验 |
@InetAddress |
IP 地址校验 | |
文件系统 | @Directory |
校验是否为目录 |
@NotDirectory |
校验是否为非目录 |
在 User
类中应用扩展约束:
public class User {
@NotNull
@Email
private String email;
@NotEmpty
private String password;
@Size(min=1, max=20)
private String name;
@Min(18)
private int age;
@Visa
private String cardNumber = "";
@IBAN
private String iban = "";
@InetAddress
private String website = "";
@Directory
private File mainDirectory = new File(".");
// 标准构造器、getter/setter
}
测试扩展约束:
@Test
public void whenValidateNonJSR_thenCorrect() {
User user = new User("user@example.com", "pass", "Ana", 20);
user.setCardNumber("1234");
user.setIban("1234");
user.setWebsite("10.0.2.50");
user.setMainDirectory(new File("."));
Set<ConstraintViolation<User>> violations
= validator.validateProperty(user,"iban");
assertEquals("size is not 1", 1, violations.size());
violations = validator.validateProperty(user,"website");
assertEquals("size is not 0", 0, violations.size());
violations = validator.validateProperty(user, "mainDirectory");
assertEquals("size is not 0", 0, violations.size());
}
⚠️ 踩坑提醒:非 JSR 约束虽强大,但会降低代码可移植性。若未来需切换其他 JSR 实现(如 Hibernate Validator),需重构这些约束。
6. 自定义约束
6.1. 定义约束注解
创建 @Password
注解,声明密码校验规则:
@Constraint(validatedBy = { PasswordValidator.class })
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
String message() default "Invalid password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int length() default 6;
int nonAlpha() default 1;
}
6.2. 实现校验逻辑
通过 ConstraintValidator
接口实现校验逻辑:
public class PasswordValidator
implements ConstraintValidator<Password, String> {
private int length;
private int nonAlpha;
@Override
public void initialize(Password password) {
this.length = password.length();
this.nonAlpha = password.nonAlpha();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value.length() < length) {
return false;
}
int nonAlphaNr = 0;
for (int i = 0; i < value.length(); i++) {
if (!Character.isLetterOrDigit(value.charAt(i))) {
nonAlphaNr++;
}
}
if (nonAlphaNr < nonAlpha) {
return false;
}
return true;
}
}
6.3. 应用自定义约束
在 User
类中使用 @Password
:
@Password(length = 8)
private String password;
6.4. 测试自定义约束
@Test
public void givenInvalidPassword_whenValidatePassword_thenConstraintViolation() {
User user = new User("user@example.com", "password", "Ana", 20);
Set<ConstraintViolation<User>> violations
= validator.validateProperty(user, "password");
assertEquals(
"message incorrect",
"Invalid password",
violations.iterator().next().getMessage());
}
@Test
public void givenValidPassword_whenValidatePassword_thenNoConstraintViolation() {
User user = new User("user@example.com", "password#", "Ana", 20);
Set<ConstraintViolation<User>> violations
= validator.validateProperty(user, "password");
assertEquals("size is not 0", 0, violations.size());
}
7. 总结
本文系统介绍了 Apache BVal 的核心功能:
- ✅ 标准 JSR 349 约束的实现
- ✅ 高级扩展约束(邮箱、信用卡、IBAN 等)
- ✅ 自定义约束开发实践
- ✅ 线程安全的校验器使用模式
完整示例代码见 GitHub 仓库。