1. 简介
Yavi 是一个轻量级 Java 验证库,能以简洁优雅的方式确保对象状态的有效性。作为 Java 应用中的理想验证方案,它具备以下核心优势:
✅ 零反射设计:无需在待验证对象上添加注解,实现完全解耦
✅ 类型安全 API:编译时就能防止无效验证规则定义
✅ 全类型支持:兼容应用中定义的所有类型
✅ 丰富约束集:提供大量预定义约束,同时支持自定义扩展
本文将深入探讨 Yavi 的核心功能与实战技巧,助你高效处理各类验证场景。
2. 依赖配置
当前最新版本为 0.14.1,在 Maven 项目中添加以下依赖:
<dependency>
<groupId>am.ik.yavi</groupId>
<artifactId>yavi</artifactId>
<version>0.14.1</version>
</dependency>
配置完成后即可开始使用。
3. 基础验证
3.1 简单值验证
针对 String
、Integer
等基础类型,使用专用构建器创建验证器:
StringValidator<String> validator = StringValidatorBuilder.of("name", c -> c.notBlank())
.build();
构建器参数说明:
- 首参数:字段名称(用于错误报告)
- 次参数:验证规则 Lambda 表达式
3.2 对象验证
更常见的场景是验证整个对象,使用 ValidatorBuilder
定义多字段规则:
public record Person(String name, int age) {}
Validator<Person> validator = ValidatorBuilder.of(Person.class)
.constraint(Person::name, "name", c -> c.notBlank())
.constraint(Person::age, "age", c -> c.positiveOrZero().lessThan(150))
.build();
关键点:
constraint()
参数依次为:字段 getter、字段名、验证规则- Yavi 自动校验 getter 返回类型与约束的兼容性(如
notBlank()
仅适用于字符串)
验证调用示例:
ConstraintViolations result = validator.validate(new Person("", 42));
assertFalse(result.isValid());
错误信息解析:
assertEquals(1, result.size());
assertEquals("name", result.get(0).name());
assertEquals("charSequence.notBlank", result.get(0).messageKey());
3.3 嵌套对象验证
当对象包含其他对象时,使用 nest()
方法进行嵌套验证:
public record Name(String firstName, String surname) {}
public record Person(Name name, int age) {}
Validator<Name> nameValidator = ValidatorBuilder.of(Name.class)
.constraint(Name::firstName, "firstName", c -> c.notBlank())
.constraint(Name::surname, "surname", c -> c.notBlank())
.build();
Validator<Person> personValidator = ValidatorBuilder.of(Person.class)
.nest(Person::name, "name", nameValidator)
.constraint(Person::age, "age", c -> c.positiveOrZero().lessThan(150))
.build();
错误路径会自动拼接:
assertEquals(2, result.size());
assertEquals("name.firstName", result.get(0).name());
assertEquals("name.surname", result.get(1).name());
3.4 跨字段验证
当验证规则依赖多个字段时,使用 constraintOnTarget()
:
record Range(int start, int end) {}
Validator<Range> validator = ValidatorBuilder.of(Range.class)
.constraintOnTarget(range -> range.end > range.start, "end", "range.endGreaterThanStart",
"\"end\" must be greater than \"start\"")
.build();
自定义约束参数:
- 首参数:验证逻辑 Lambda
- 次参数:字段名
- 第三参数:错误消息键
- 第四参数:默认错误消息
4. 自定义约束
4.1 内联自定义约束
通过 predicate()
方法快速定义临时约束:
Validator<Data> validator = ValidatorBuilder.of(Data.class)
.constraint(Data::palindrome, "palindrome",
c -> c.predicate(s -> validatePalindrome(s), "palindrome.valid", "\"{0}\" must be a palindrome"))
.build();
4.2 可复用约束类
实现 CustomConstraint
接口创建可复用约束:
class PalindromeConstraint implements CustomConstraint<String> {
@Override
public boolean test(String input) {
String reversed = new StringBuilder()
.append(input)
.reverse()
.toString();
return input.equals(reversed);
}
@Override
public String messageKey() {
return "palindrome.valid";
}
@Override
public String defaultMessageFormat() {
return "\"{0}\" must be a palindrome";
}
}
使用方式:
Validator<Data> validator = ValidatorBuilder.of(Data.class)
.constraint(Data::palindrome, "palindrome", c -> c.predicate(new PalindromeConstraint()))
.build();
5. 条件约束
5.1 基于上下文的约束
定义约束组(推荐使用枚举):
enum Action implements ConstraintGroup {
CREATE,
UPDATE,
DELETE
}
使用 constraintOnCondition()
指定生效条件:
Validator<Person> validator = ValidatorBuilder.of(Person.class)
.constraint(Person::name, "name", c -> c.notBlank())
.constraintOnCondition(Action.UPDATE.toCondition(),
b -> b.constraint(Person::id, "id", c -> c.notBlank()))
.build();
验证时传入上下文:
ConstraintViolations result = validator.validate(new Person(null, "Baeldung"), Action.UPDATE);
5.2 动态条件约束
通过 Lambda 定义复杂条件:
Validator<Person> validator = ValidatorBuilder.of(Person.class)
.constraintOnCondition((person, ctx) -> person.id() != null,
b -> b.constraint(Person::name, "name", c -> c.notBlank()))
.build();
6. 方法参数验证
Yavi 支持在方法调用前验证参数,避免创建无效对象:
6.1 构造函数验证
Arguments2Validator<String, Integer, Person> validator = ArgumentsValidatorBuilder.of(Person::new)
.builder(b -> b
._string(Arguments1::arg1, "name", c -> c.notBlank())
._integer(Arguments2::arg2, "age", c -> c.positiveOrZero())
)
.build();
验证调用:
Validated<Person> result = validator.validate("", -1);
错误处理:
assertFalse(result.isValid());
assertEquals(2, result.errors().size());
assertEquals("name", result.errors().get(0).name());
assertEquals("charSequence.notBlank", result.errors().get(0).messageKey());
6.2 普通方法验证
record Person(String name, int age) {
boolean isOlderThan(int check) {
return this.age > check;
}
}
Arguments2Validator<Person, Integer, Boolean> validator = ArgumentsValidatorBuilder.of(Person::isOlderThan)
.builder(b -> b
._integer(Arguments2::arg2, "age", c -> c.positiveOrZero())
)
.build();
调用方式:
Person person = new Person("Baeldung", 42);
Validated<Boolean> result = validator.validate(person, -1);
7. 注解处理器
7.1 字段注解
使用 @ConstraintTarget
自动生成元类:
record Person(@ConstraintTarget String name, @ConstraintTarget int age) {}
简化验证器构建:
Validator<Person> validator = ValidatorBuilder.of(Person.class)
.constraint(_PersonMeta.NAME, c -> c.notBlank())
.constraint(_PersonMeta.AGE, c -> c.positiveOrZero().lessThan(150))
.build();
优势:
- 无需手动指定字段名和 getter
- 字段不存在时编译报错
7.2 方法参数注解
使用 @ConstraintArguments
注解方法或构造函数:
record Person(String name, int age) {
@ConstraintArguments
Person {}
@ConstraintArguments
boolean isOlderThan(int check) {
return this.age() > check;
}
}
生成验证器:
Arguments2Validator<String, Integer, Person> validator = ArgumentsValidatorBuilder.of(Person::new)
.builder(b -> b
.constraint(_PersonArgumentsMeta.NAME, c -> c.notBlank())
.constraint(_PersonArgumentsMeta.AGE, c -> c.positiveOrZero())
)
.build();
8. 总结
Yavi 作为现代 Java 验证框架,通过类型安全 API、零反射设计和灵活的扩展机制,为开发者提供了高效可靠的验证解决方案。其核心优势包括:
🚀 简洁性:流式 API 构建验证规则,代码可读性强
🔧 灵活性:支持嵌套对象、跨字段验证和条件约束
⚡ 高性能:编译时生成代码,避免运行时反射开销
🌐 生态集成:完美兼容 Spring 等主流框架
下次需要验证框架时,不妨尝试 Yavi,体验其带来的开发效率提升。所有示例代码可在 GitHub 获取。