Micronaut Validation is a customizable validation solution for your applications.
Note
Micronaut Validation was moved to a separate module for Micronaut version 4.0.0. For previous versions of Micronaut, refer to the Micronaut User Guide.
2 Release History
For this project, you can find a list of releases (with release notes) here:
You can validate types, fields and parameters by applying jakarta.validation annotations to arguments. The jakarta.validation-api library exposes those annotations, but it unnecessary to specify it as a direct dependency. It is included transitively when using micronaut-validation.
Supported Features
Note that Micronaut’s implementation is not currently fully compliant with the Bean Validator specification as the specification heavily relies on reflection-based APIs.
The following features are unsupported at this time:
Any interaction with the constraint metadata API, since Micronaut uses compile-time generated metadata.
XML-based configuration
Instead of using jakarta.validation.ConstraintValidator, use ConstraintValidator (io.micronaut.validation.validator.constraints.ConstraintValidator) to define custom constraints, which supports validating annotations at compile time.
Micronaut’s implementation includes the following benefits:
Reflection and Runtime Proxy free validation, resulting in reduced memory consumption
Smaller JAR size since Hibernate Validator adds another 1.4MB
Faster startup since Hibernate Validator adds 200ms+ startup overhead
Configurability via Annotation Metadata
Support for Reactive Bean Validation
Support for validating the source AST at compile time
Automatic compatibility with GraalVM native without additional configuration
If you require full Bean Validator 2.0 compliance, add the micronaut-hibernate-validator module to your build, which replaces Micronaut’s implementation.
You can validate methods of any class declared as a Micronaut bean by applying jakarta.validation annotations to arguments:
Validating Methods
import jakarta.inject.Singleton;import jakarta.validation.constraints.NotBlank;@Singletonpublic class PersonService { public void sayHello(@NotBlank String name) { System.out.println("Hello " + name); }}
import jakarta.inject.Singletonimport jakarta.validation.constraints.NotBlank@Singletonopen class PersonService { open fun sayHello(@NotBlank name: String) { println("Hello $name") }}
The above example declares that the @NotBlank annotation will be validated when invoking the sayHello method.
Warning
If you use Kotlin, the class and method must be declared open so Micronaut can create a compile-time subclass. Alternatively you can annotate the class with @Validated and configure the Kotlin all-open plugin to open classes annotated with this type. See the Compiler plugins section.
A jakarta.validation.ConstraintViolationException is thrown if a validation error occurs. For example:
ConstraintViolationException Example
5 Validating Data Classes
To validate data classes, e.g. POJOs (typically used in JSON interchange), the class must be annotated with @Introspected (see Micronaut Guide Introspection section) or, if the class is external, be imported by the @Introspected annotation.
POJO Validation Example
import io.micronaut.core.annotation.Introspected;import jakarta.validation.constraints.Min;import jakarta.validation.constraints.NotBlank;@Introspectedpublic class Person { private String name; @Min(18) private int age; @NotBlank public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; }}
import io.micronaut.core.annotation.Introspectedimport jakarta.validation.constraints.Minimport jakarta.validation.constraints.NotBlank@Introspecteddata class Person( @field:NotBlank var name: String, @field:Min(18) var age: Int)
import io.micronaut.core.annotation.Introspectedimport jakarta.validation.constraints.Minimport jakarta.validation.constraints.NotBlank@Introspectedclass Person { @NotBlank String name @Min(18L) int age}
Tip
The @Introspected annotation can be used as a meta-annotation; common annotations like @jakarta.persistence.Entity are treated as @Introspected
The above example defines a Person class that has two properties (name and age) that have constraints applied. Note that in Java the annotations can be on the field or the getter, and with Kotlin data classes, the annotation should target the field.
To validate the class manually, inject an instance of Validator:
Manual Validation Example
Alternatively on Bean methods you can use jakarta.validation.Valid to trigger cascading validation:
ConstraintViolationException Example
@Singletonpublic class PersonService { public void sayHello(@Valid Person person) { System.out.println("Hello " + person.getName()); }}
@Singletonopen class PersonService { open fun sayHello(@Valid person: Person) { println("Hello ${person.name}") }}
A constraint with an empty validatedBy = {} will use the bean context to find a bean of ConstraintValidator with a generic value of your annotation, or it’s possible to set validatedBy = MyConstraintValidator.class and in this case the validator can be an introspected bean or a simple bean in loaded from the bean context.
Once you have defined the annotation, implement a ConstraintValidator that validates the annotation. You can either create a bean class that implements the interface directly or define a factory that returns one or more validators.
The former approach can be taken for single bean:
Example Constraint Validator Bean
The latter approach is recommended if you plan to define multiple validators:
Example Constraint Validator Factory
The above example implements a validator that validates any field, parameter etc. that is annotated with DurationPattern, ensuring that the string can be parsed with java.time.Duration.parse.
Note
Generally null is regarded as valid and @NotNull is used to constrain a value as not being null. The example above regards null as a valid value.
For example:
Example Custom Constraint Usage
@Singletonpublic class HolidayService { @Executable public String startHoliday(@NotBlank String person, @DurationPattern String duration) { final Duration d = Duration.parse(duration); return "Person " + person + " is off on holiday for " + d.toMinutes() + " minutes"; } public String startHoliday(@DurationPattern String fromDuration, @DurationPattern String toDuration, @NotBlank String person ) { final Duration d = Duration.parse(fromDuration); final Duration e = Duration.parse(toDuration); return "Person " + person + " is off on holiday from " + d + " to " + e; }}
@Singletonopen class HolidayService { open fun startHoliday(@NotBlank person: String, @DurationPattern duration: String): String { val d = Duration.parse(duration) return "Person $person is off on holiday for ${d.toMinutes()} minutes" }}
@Singletonclass HolidayService { String startHoliday(@NotBlank String person, @DurationPattern String duration) { final Duration d = Duration.parse(duration) return "Person $person is off on holiday for ${d.toMinutes()} minutes" }}
To verify the above examples validates the duration parameter, define a test:
Micronaut Validation provides additional constraints in the io.micronaut.validation.annotation package. Like other Bean Validation constraints, these constraints consider null values valid. Use jakarta.validation.constraints.NotNull when a value must be present.
Requires a String value to match a constant name in the enum supplied through value. An instance of that enum is also valid. Set caseSensitive to false for a case-insensitive string comparison.
Rejects a String value that matches a constant name in the enum supplied through value. Set caseSensitive to false for a case-insensitive string comparison.
Requires a character sequence to be a valid URL. Use protocol, host, and port to restrict the corresponding URL components. Use regexp and flags to apply an additional regular-expression restriction. The annotation may be repeated on the same element.
9 Validating Annotations at Compile Time
Micronaut Validation validates annotation elements at compile time with micronaut-validation-processor in the annotation processor classpath:
Micronaut Validation will, at compile time, validate annotation values that are themselves annotated with jakarta.validation.
For example consider the following annotation:
If you attempt to use @TimeOff(duration="junk") in your source, Micronaut will fail compilation due to the duration value violating the DurationPattern constraint.
Note
If duration is a property placeholder such as @TimeOff(duration="${my.value}"), validation is deferred until runtime.
Note that to use a custom ConstraintValidator at compile time you must instead define the validator as a class:
Define a META-INF/services/io.micronaut.validation.validator.constraints.ConstraintValidator file that references the class.
The class must be public and have a public no-argument constructor
The class must be on the annotation processor classpath of the project to be validated.
10 Breaking Changes
This section documents breaking changes between Micronaut Validation versions:
Micronaut Validation 5.0.0
The DefaultValidator methods requireNonNull(String, T) and requireNonEmpty(String, String) deprecated previously and are no longer exposed as part of the public API.
The ConstraintValidator method getMessageTemplate()) and requireNonEmpty(String, String) deprecated previously is removed. It was unused and has no replacement.
11 Repository
You can find the source code of this project in this repository: