Jakarta Validation API defines a metadata model and API for JavaBean and method validation
—
Validation support for method parameters, return values, and constructor parameters with cross-parameter constraint capabilities and executable validation configuration.
Interface for validating method and constructor parameters and return values.
/**
* Validates parameters and return values of methods and constructors
* Obtained from Validator.forExecutables()
*/
interface ExecutableValidator {
/**
* Validate method parameters
* @param object instance on which the method is invoked (null for static methods)
* @param method method to validate parameters for
* @param parameterValues parameter values to validate
* @param groups validation groups to apply
* @return set of constraint violations
*/
<T> Set<ConstraintViolation<T>> validateParameters(
T object, Method method, Object[] parameterValues, Class<?>... groups);
/**
* Validate method return value
* @param object instance on which the method was invoked (null for static methods)
* @param method method to validate return value for
* @param returnValue return value to validate
* @param groups validation groups to apply
* @return set of constraint violations
*/
<T> Set<ConstraintViolation<T>> validateReturnValue(
T object, Method method, Object returnValue, Class<?>... groups);
/**
* Validate constructor parameters
* @param constructor constructor to validate parameters for
* @param parameterValues parameter values to validate
* @param groups validation groups to apply
* @return set of constraint violations
*/
<T> Set<ConstraintViolation<T>> validateConstructorParameters(
Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups);
/**
* Validate constructor return value (the created object)
* @param constructor constructor that created the object
* @param createdObject the created object to validate
* @param groups validation groups to apply
* @return set of constraint violations
*/
<T> Set<ConstraintViolation<T>> validateConstructorReturnValue(
Constructor<? extends T> constructor, T createdObject, Class<?>... groups);
}Enums and annotations for configuring when executable validation occurs.
/**
* Defines the types of executables targeted by an operation
*/
enum ExecutableType {
/** No executable validation */
NONE,
/** Implicit configuration (provider-specific) */
IMPLICIT,
/** All constructors */
CONSTRUCTORS,
/** All methods except getters */
NON_GETTER_METHODS,
/** Getter methods only */
GETTER_METHODS,
/** All executables (constructors and methods) */
ALL
}
/**
* Controls executable validation on methods and constructors
* Can be applied to types, methods, and constructors
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(RUNTIME)
@interface ValidateOnExecution {
/**
* Define the type of executables to validate
* @return array of ExecutableType values
*/
ExecutableType[] type() default {ExecutableType.IMPLICIT};
}Support for constraints that validate multiple parameters together.
/**
* Validation target enum for constraint validators
*/
enum ValidationTarget {
/** Validate the annotated element */
ANNOTATED_ELEMENT,
/** Validate the array of parameters (cross-parameter validation) */
PARAMETERS
}
/**
* Defines targets a ConstraintValidator can validate
* Applied to ConstraintValidator implementations
*/
@Target({TYPE})
@Retention(RUNTIME)
@interface SupportedValidationTarget {
/**
* Supported validation targets
* @return array of ValidationTarget values
*/
ValidationTarget[] value();
}
/**
* Defines constraint target (parameters vs return value)
*/
enum ConstraintTarget {
/** Let the validation engine determine the target */
IMPLICIT,
/** Target the method/constructor parameters */
PARAMETERS,
/** Target the method return value */
RETURN_VALUE
}Usage Examples:
import jakarta.validation.*;
import jakarta.validation.constraints.*;
import jakarta.validation.executable.*;
import java.lang.reflect.Method;
// 1. Method parameter validation
@ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
public class UserService {
public User createUser(
@NotNull @Size(min = 2, max = 50) String name,
@Min(18) int age,
@Email String email) {
return new User(name, age, email);
}
@NotNull
@Valid
public User updateUser(@NotNull @Valid User user) {
// Update logic
return user;
}
// Getter - validation controlled by ExecutableType.GETTER_METHODS
@Size(min = 1)
public List<User> getUsers() {
return userRepository.findAll();
}
}
// 2. Constructor validation
public class Product {
private String name;
private BigDecimal price;
public Product(
@NotBlank String name,
@DecimalMin("0.01") BigDecimal price) {
this.name = name;
this.price = price;
}
}
// 3. Cross-parameter validation
@Target({METHOD, CONSTRUCTOR})
@Retention(RUNTIME)
@Constraint(validatedBy = {DateRangeValidator.class})
public @interface ValidDateRange {
String message() default "End date must be after start date";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class DateRangeValidator implements ConstraintValidator<ValidDateRange, Object[]> {
@Override
public boolean isValid(Object[] parameters, ConstraintValidatorContext context) {
if (parameters.length != 2) {
return false;
}
LocalDate startDate = (LocalDate) parameters[0];
LocalDate endDate = (LocalDate) parameters[1];
if (startDate == null || endDate == null) {
return true; // Let @NotNull handle null values
}
return endDate.isAfter(startDate);
}
}
// Usage of cross-parameter validation
public class BookingService {
@ValidDateRange
public Booking createBooking(
@NotNull LocalDate startDate,
@NotNull LocalDate endDate,
@NotNull String guestName) {
return new Booking(startDate, endDate, guestName);
}
}
// 4. Manual validation using ExecutableValidator
public class ValidationExample {
private Validator validator;
private ExecutableValidator executableValidator;
public ValidationExample() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
this.executableValidator = validator.forExecutables();
}
public void validateMethodCall() throws Exception {
UserService userService = new UserService();
Method createUserMethod = UserService.class.getMethod(
"createUser", String.class, int.class, String.class);
// Validate parameters before method call
Object[] parameters = {"", 15, "invalid-email"};
Set<ConstraintViolation<UserService>> parameterViolations =
executableValidator.validateParameters(
userService, createUserMethod, parameters);
if (!parameterViolations.isEmpty()) {
System.out.println("Parameter validation failed:");
for (ConstraintViolation<UserService> violation : parameterViolations) {
System.out.println(" " + violation.getPropertyPath() + ": " +
violation.getMessage());
}
return;
}
// Call method
User result = userService.createUser("Alice", 25, "alice@example.com");
// Validate return value
Set<ConstraintViolation<UserService>> returnValueViolations =
executableValidator.validateReturnValue(
userService, createUserMethod, result);
if (!returnValueViolations.isEmpty()) {
System.out.println("Return value validation failed:");
for (ConstraintViolation<UserService> violation : returnValueViolations) {
System.out.println(" " + violation.getMessage());
}
}
}
public void validateConstructor() throws Exception {
Constructor<Product> constructor = Product.class.getConstructor(
String.class, BigDecimal.class);
// Validate constructor parameters
Object[] parameters = {"", new BigDecimal("-1.00")};
Set<ConstraintViolation<Product>> violations =
executableValidator.validateConstructorParameters(constructor, parameters);
if (!violations.isEmpty()) {
System.out.println("Constructor parameter validation failed:");
for (ConstraintViolation<Product> violation : violations) {
System.out.println(" " + violation.getPropertyPath() + ": " +
violation.getMessage());
}
return;
}
// Create object
Product product = constructor.newInstance("Valid Product", new BigDecimal("29.99"));
// Validate created object
Set<ConstraintViolation<Product>> objectViolations =
executableValidator.validateConstructorReturnValue(constructor, product);
if (objectViolations.isEmpty()) {
System.out.println("Product created successfully");
}
}
}
// 5. Configuration at class and method level
@ValidateOnExecution(type = {ExecutableType.CONSTRUCTORS, ExecutableType.NON_GETTER_METHODS})
public class OrderService {
// This constructor will be validated (due to class-level annotation)
public OrderService(@NotNull String serviceName) {
// Constructor logic
}
// This method will be validated (due to class-level annotation)
public Order processOrder(@NotNull @Valid Order order) {
return order;
}
// Override class-level setting - no validation for this method
@ValidateOnExecution(type = ExecutableType.NONE)
public void internalMethod(String param) {
// Internal method with no validation
}
// This getter will NOT be validated (not included in class-level annotation)
public String getServiceName() {
return "OrderService";
}
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-validation--jakarta-validation-api