Introduction

Input validation is one of the most fundamental and frequently repeated tasks in enterprise Java development. Whether you’re building REST APIs, processing user forms, or handling configuration files, ensuring data integrity through validation prevents bugs, security vulnerabilities, and data corruption downstream. The Java ecosystem offers several validation libraries, each with different philosophies — from annotation-driven declarative validation to fluent lambda-based APIs.

This comparison examines five Java validation libraries: Hibernate Validator (1,265 GitHub stars), the reference implementation of Jakarta Bean Validation; Jakarta Validation (164 stars), the specification API itself; Yavi (847 stars), a lambda-based type-safe validation framework; Valiktor (449 stars), a Kotlin-friendly fluent DSL; and Avaje Validator (59 stars), an annotation-processing-based approach that generates code at compile time.

Comparison Table

FeatureHibernate ValidatorJakarta ValidationYaviValiktorAvaje Validator
Stars1,26516484744959
LanguageJavaJavaJavaKotlinJava
Validation StyleAnnotationsSpecification APILambda expressionsFluent DSLAnnotation Processing
Standard ComplianceJakarta Bean Validation 3.0Jakarta Bean Validation 3.0CustomCustomCustom
Runtime vs Compile-timeRuntimeRuntimeRuntimeRuntimeCompile-time
Constraint CompositionYesYesYesYesLimited
Custom ValidatorsYesYesYesYesYes
Spring Boot IntegrationBuilt-inVia Hibernate ValidatorManualManualManual
Kotlin SupportGoodVia Hibernate ValidatorGoodExcellentGood
Last Release2026-072026-062026-012021-122026-06
LicenseApache 2.0Apache 2.0Apache 2.0Apache 2.0Apache 2.0

Hibernate Validator: The Industry Standard

Hibernate Validator is the reference implementation of Jakarta Bean Validation and ships as the default validation engine in Spring Boot, Quarkus, and most Jakarta EE application servers. It supports all standard constraint annotations (@NotNull, @Size, @Email, @Pattern, etc.) plus a rich set of extensions.

For Maven users, add Hibernate Validator with its Expression Language dependency:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.2.Final</version>
</dependency>
<dependency>
    <groupId>org.glassfish.expressly</groupId>
    <artifactId>expressly</artifactId>
    <version>5.0.0</version>
</dependency>

A typical domain model with validation constraints:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class UserRegistration {
    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 50)
    private String username;

    @Email(message = "Must be a valid email")
    @NotBlank
    private String email;

    @Min(value = 18, message = "Must be at least 18")
    private int age;

    @Pattern(regexp = "^(?=.*[A-Z])(?=.*\\d).{8,}$")
    private String password;
}

Strengths: Massive ecosystem adoption, excellent documentation, Spring Boot auto-configuration, internationalized error messages, and support for method-level validation (@Validated on service classes). The ConstraintValidator interface makes it easy to create custom constraints with full dependency injection support.

Weaknesses: Annotation-based validation requires reflection at runtime, which adds overhead. Complex cross-field validation rules (e.g., “end date must be after start date”) require class-level annotations that are verbose. Error messages require ValidationMessages.properties files for internationalization.

Jakarta Validation API

Jakarta Validation (formerly javax.validation) is the specification API that Hibernate Validator implements. You typically don’t use it directly — instead, you reference the API in your code while Hibernate Validator provides the implementation at runtime:

1
2
3
4
5
<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>3.0.2</version>
</dependency>

The separation between API and implementation allows swapping validation providers without changing code. However, in practice, Hibernate Validator is the only widely used implementation, making this abstraction mostly theoretical for most projects.

Yavi: Lambda-Based Type-Safe Validation

Yavi takes a fundamentally different approach — instead of annotations, you define validation rules using Java lambda expressions. This makes validation logic explicit, testable, and compile-time safe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Validator<UserRegistration> validator = ValidatorBuilder.<UserRegistration>of()
    .constraint(UserRegistration::getUsername, "username",
        c -> c.notNull().greaterThanOrEqual(3).lessThanOrEqual(50))
    .constraint(UserRegistration::getEmail, "email",
        c -> c.notNull().email())
    .constraint(UserRegistration::getAge, "age",
        c -> c.notNull().greaterThanOrEqual(18))
    .build();

// Validate and get all violations
ConstraintViolations violations = validator.validate(user);
if (violations.isValid()) {
    // proceed
} else {
    violations.forEach(v -> System.out.println(v.message()));
}

Yavi supports nested object validation through constraintOnObject, conditional validation rules with constraintOnCondition, and all standard constraints. It also supports Java 17+ features including records and sealed classes.

Strengths: Complete type safety at compile time, no reflection overhead, validation rules are easy to read and test, and lambda-based API enables functional composition of validators.

Weaknesses: No annotation-based approach means you can’t use Yavi with Spring Boot’s automatic validation in controller methods (@Valid on @RequestBody). Requires explicit validator invocation. Smaller community and ecosystem compared to Hibernate Validator.

Valiktor: Fluent DSL for JVM Languages

Valiktor provides a fluent, type-safe validation DSL written in Kotlin but fully usable from Java. It emphasizes immutability and functional composition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
data class Employee(val name: String, val age: Int, val email: String)

fun validate(employee: Employee) {
    validate(employee) {
        validate(Employee::name) {
            notBlank()
            size(3, 50)
        }
        validate(Employee::age) {
            isPositive()
            isGreaterThanOrEqualTo(18)
        }
        validate(Employee::email) {
            isEmail()
        }
    }
}

Valiktor stands out for Kotlin-first development teams. Its constraint library covers the full range of validation needs including Brazilian CPF/CNPJ validation, credit card numbers, and ISBN codes.

Weaknesses: Last updated in December 2021 — the project appears unmaintained. Kotlin dependency adds overhead for pure Java projects. No Spring Boot integration.

Avaje Validator: Compile-Time Validation

Avaje Validator generates validation code at compile time via annotation processing, eliminating reflection overhead entirely:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Valid
public class CreateOrderRequest {
    @NotBlank @Size(max = 100)
    private String productName;

    @Positive
    private int quantity;

    @NotBlank
    private String customerId;
}

The annotation processor generates a CreateOrderRequestValidator class at compile time. Unlike Hibernate Validator, there’s no runtime reflection — validation is performed by generated code that’s as fast as hand-written checks.

Why Use a Dedicated Validation Library?

Data validation is a cross-cutting concern that touches every layer of an application. Using a dedicated validation library instead of scattered if statements provides several benefits. First, it centralizes validation rules so they’re consistent across all entry points — you won’t have different email format rules in your REST controller versus your service layer. Second, validation libraries produce structured error objects that can be serialized directly into API error responses, making it easy to return informative messages to clients. Third, constraint composition allows building complex validation rules from simple building blocks — a @ValidCustomer annotation could combine email validation, phone number format checking, and postal code verification into a single reusable constraint.

For Java developers working with microservices, see our guide on Java web frameworks comparison which covers how each framework handles validation integration differently. If you’re working with JSON APIs, our Java JSON libraries comparison shows how validation errors can be serialized with Jackson or Gson. For HTTP client-side validation concerns, check our Java HTTP client libraries guide.

FAQ

Which Java validation library should I use for Spring Boot?

Hibernate Validator is the default and recommended choice for Spring Boot applications. It’s pre-configured in spring-boot-starter-validation and integrates seamlessly with @Valid and @Validated annotations on controller methods. Spring Boot’s error handling automatically converts MethodArgumentNotValidException into structured JSON error responses.

Can I use Yavi alongside Hibernate Validator in the same project?

Yes, they serve different purposes and can coexist. Use Hibernate Validator for request body validation in Spring controllers (where @Valid triggers it automatically), and Yavi for domain-level validation in service classes where you want explicit, testable validation rules without reflection overhead.

Is Valiktor still maintained?

Valiktor’s last release was in December 2021 and the GitHub repository shows no recent activity. For new projects, consider Yavi (if you prefer lambda-based validation) or Hibernate Validator (if you need Bean Validation compliance). Existing Valiktor users may want to plan a migration path.

How does Avaje Validator handle cross-field validation?

Avaje Validator supports cross-field validation through custom method annotations. Unlike Hibernate Validator’s class-level constraints, Avaje generates compile-time code for cross-field checks, keeping validation fast. However, the constraint composition system is less mature than Hibernate Validator’s, so complex cross-field rules may require manual implementation.

What’s the performance difference between runtime and compile-time validation?

Compile-time validation (Avaje) eliminates reflection overhead entirely — validation executes as plain Java method calls. This is significant for high-throughput APIs processing thousands of requests per second. Hibernate Validator’s reflection overhead is typically negligible for most applications, but profiling shows it contributes 2-5% of CPU time in request-heavy microservices.

Can I write custom validation annotations with Hibernate Validator?

Yes, implement the ConstraintValidator<A extends Annotation, T> interface and annotate it with @Constraint(validatedBy = YourValidator.class). This gives you full access to the validated object, dependency injection, and all Jakarta Bean Validation features. Custom constraints can compose multiple built-in validators internally.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com