Annotation-based validation provides a declarative way to define validation rules directly on your class fields. This approach is perfect for DTOs, POJOs, and data models where validation requirements are well-defined.
How Annotations Work
Validator scans your class fields at runtime using Java Reflection, evaluating each annotation in the order they appear. When validation fails, an InvalidEvaluationException is thrown with details about the failing field.
Basic Usage
import io.github.ApamateSoft.validator.annotations.*;
public class User {
@Required
@Email
private String email;
@Required
@MinLength(min = 8)
private String password;
// Constructor, getters, setters...
}
Validate the object using the static method:
import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;
User user = new User();
user.setEmail("[email protected]");
user.setPassword("short");
try {
Validator.validOrFail(user);
// All fields validated successfully
} catch (InvalidEvaluationException e) {
System.out.println("Field: " + e.getKey()); // "password"
System.out.println("Value: " + e.getValue()); // "short"
System.out.println("Error: " + e.getMessage()); // "8 or more characters are required"
}
Validator only processes fields of type String. All other field types are ignored during validation.
Available Annotations
Validator provides 27 annotations that mirror the predefined rules in the Builder API.
Existence and Length
@Required
@Length
@MinLength
@MaxLength
@RangeLength
Validates that the String is not null and not empty.@Required
private String username;
@Required(message = "Email is required")
private String email;
Validates exact character length.@Length(length = 5)
private String zipCode;
@Length(length = 10, message = "Must be exactly 10 characters")
private String accountNumber;
Validates minimum character length.@MinLength(min = 8)
private String password;
@MinLength(min = 3, message = "At least 3 characters required")
private String name;
Validates maximum character length.@MaxLength(max = 255)
private String email;
@MaxLength(max = 100, message = "Maximum 100 characters")
private String bio;
Validates that length falls within a range.@RangeLength(min = 3, max = 20)
private String username;
@RangeLength(min = 10, max = 500, message = "Must be between 10-500 chars")
private String description;
@Email
@Number
@Date
@RegExp
Validates email format.@Required
@Email
private String email;
@Email(message = "Invalid email address")
private String contactEmail;
Validates numeric format (integers, decimals, negative values).@Number
private String price;
@Number(message = "Must be a valid number")
private String quantity;
Validates date format.@Date(format = "dd/MM/yyyy")
private String birthDate;
@Date(format = "yyyy-MM-dd", message = "Invalid date format")
private String registrationDate;
Validates against a regular expression.@RegExp(regExp = "[A-Z]{3}[0-9]{3}")
private String code;
@RegExp(regExp = "^[a-z]+$", message = "Only lowercase letters")
private String tag;
Content Validation
@OnlyNumbers
@OnlyLetters
@OnlyAlphanumeric
Validates that String contains only numeric characters.@Required
@OnlyNumbers
private String zipCode;
@OnlyNumbers(message = "Numbers only")
private String pin;
Validates that String contains only letters.@OnlyLetters
private String firstName;
@OnlyLetters(message = "Letters only")
private String lastName;
Validates that String contains only letters and numbers.@OnlyAlphanumeric
private String username;
@OnlyAlphanumeric(message = "Letters and numbers only")
private String productCode;
Network and Links
@Link
private String website;
@WwwLink
private String companyWebsite;
@HttpLink
private String httpEndpoint;
@HttpsLink
private String secureEndpoint;
@Ip
private String serverIp;
@Ipv4
private String ipv4Address;
@Ipv6
private String ipv6Address;
Time Validation
@Time
private String appointmentTime;
@Time12
private String time12Hour;
@Time24
private String time24Hour;
Advanced Annotations
@NumberPattern
Validates that String matches a pattern where x represents numbers:
@Required
@NumberPattern(patter = "(xxxx) xxx-xx-xx")
private String phone;
@NumberPattern(patter = "+xx (xxx) xxx-xx-xx", message = "Invalid phone format")
private String internationalPhone;
Valid values for pattern (xxxx) xxx-xx-xx:
(1234) 567-89-01
(xxxx) 567-89-01
(xxxx) xxx-xx-xx
@Name
Validates proper name format:
@Required
@Name
private String fullName;
@Name(message = "Invalid name format")
private String authorName;
Accepts formats like: “Jesus”, “Maria”, “JOSE”, “Jesus Maria”, “Maria Jose”
@Compare
Compares with another field in the same class:
@Required
@MinLength(min = 8)
@Compare(compareWith = "passwordConfirm", message = "Passwords do not match")
private String password;
private String passwordConfirm;
The compareWith attribute must exactly match the name of another field in the class.
@MustContainOne
Validates that String contains at least one character from the specified alphabet:
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
@Required
@MinLength(min = 8)
@MustContainOne(alphabet = ALPHA_UPPERCASE, message = "At least one uppercase letter required")
@MustContainOne(alphabet = "@#*-", message = "At least one special character required")
private String password;
@MustContainMin
Validates minimum number of characters from the specified alphabet:
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
@Required
@MinLength(min = 8)
@MustContainMin(min = 3, alphabet = ALPHA_LOWERCASE, message = "At least 3 lowercase letters")
@MustContainMin(min = 3, alphabet = NUMBER, message = "At least 3 numbers")
@MustContainMin(min = 1, alphabet = ALPHA_UPPERCASE, message = "At least 1 uppercase letter")
private String strongPassword;
@NotContain
Validates that String does not contain any of the specified characters:
@Required
@NotContain(alphabet = "<>")
private String username;
@NotContain(alphabet = "!@#$%", message = "Special characters not allowed")
private String displayName;
@ShouldOnlyContain
Validates that String only contains specified characters:
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
@ShouldOnlyContain(alphabet = NUMBER)
private String zipCode;
@ShouldOnlyContain(alphabet = ALPHA_NUMERIC, message = "Only letters and numbers")
private String identifier;
Numeric Range Validation
@MinValue and @MaxValue
@Required
@Number
@MinValue(min = 0.0)
private String price;
@Number
@MinValue(min = 18.0, message = "Must be at least 18")
@MaxValue(max = 120.0, message = "Must be at most 120")
private String age;
@RangeValue
@Required
@Number
@RangeValue(min = 0.0, max = 100.0)
private String percentage;
@RangeValue(min = 1.0, max = 10.0, message = "Must be between 1 and 10")
private String rating;
Date and Age Validation
@MinAge
Validates minimum age from date:
@Required
@Date(format = "dd/MM/yyyy")
@MinAge(format = "dd/MM/yyyy", age = 18)
private String birthDate;
@Date(format = "yyyy-MM-dd")
@MinAge(format = "yyyy-MM-dd", age = 21, message = "Must be at least 21 years old")
private String dateOfBirth;
@ExpirationDate
Validates that date has not expired:
@Required
@Date(format = "MM/yyyy")
@ExpirationDate(format = "MM/yyyy")
private String cardExpiration;
@Date(format = "yyyy-MM-dd")
@ExpirationDate(format = "yyyy-MM-dd", message = "This date has expired")
private String validUntil;
Repeatable Annotations
Some annotations can be repeated on the same field:
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
@Required
@MinLength(min = 12)
@MustContainMin(min = 3, alphabet = ALPHA_LOWERCASE)
@MustContainMin(min = 3, alphabet = ALPHA_UPPERCASE)
@MustContainMin(min = 3, alphabet = NUMBER)
@MustContainMin(min = 3, alphabet = "@~_/")
@NotContain(alphabet = "<>")
@NotContain(alphabet = "'\"")
private String securePassword;
Repeatable annotations:
@MustContainOne
@MustContainMin
@NotContain
Complete Example: Password Validation
Here’s a comprehensive example showing password validation with multiple annotations:
import io.github.ApamateSoft.validator.annotations.*;
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
public class PasswordChange {
@Required(message = "Password is required")
@MinLength(min = 8, message = "Password must be at least 8 characters")
@MustContainMin(min = 3, alphabet = ALPHA_LOWERCASE, message = "At least 3 lowercase letters required")
@MustContainMin(min = 3, alphabet = NUMBER, message = "At least 3 numbers required")
@MustContainOne(alphabet = ALPHA_UPPERCASE, message = "At least one uppercase letter required")
@MustContainOne(alphabet = "@#*-", message = "At least one special character (@#*-) required")
@Compare(compareWith = "passwordConfirm", message = "Passwords do not match")
private String password;
private String passwordConfirm;
public PasswordChange(String password, String passwordConfirm) {
this.password = password;
this.passwordConfirm = passwordConfirm;
}
// Getters and setters...
}
Validate the password change:
import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;
public class UserService {
public void changePassword(String newPassword, String confirmPassword) {
PasswordChange passwordChange = new PasswordChange(newPassword, confirmPassword);
try {
Validator.validOrFail(passwordChange);
// Password meets all requirements
updateUserPassword(newPassword);
} catch (InvalidEvaluationException e) {
displayError(e.getMessage());
}
}
}
Custom Messages vs Default Messages
All annotations support optional custom messages:
// Using default messages
@Required
@Email
private String email;
// Using custom messages
@Required(message = "Please enter your email address")
@Email(message = "The email format is invalid")
private String email;
Default messages are available in English (MessagesEn) and Spanish (MessagesEs).
You can change the default message language using Validator.setMessages(new MessagesEs()).
Annotation Evaluation Order
Annotations are evaluated top-to-bottom. When one fails, evaluation stops:
@Required // Evaluated first
@MinLength(min = 8) // Evaluated second (if @Required passes)
@Email // Evaluated third (if @MinLength passes)
private String email;
If email is null, only @Required error is returned. The other annotations are not evaluated.
Order your annotations logically: existence checks first, then format validation, then content requirements.
Complete Registration Example
import io.github.ApamateSoft.validator.annotations.*;
import static io.github.ApamateSoft.validator.utils.Alphabets.*;
public class UserRegistration {
@Required(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
@Required(message = "Phone is required")
@NumberPattern(patter = "(xxxx) xxx xx xx", message = "Invalid phone format")
private String phone;
@Required(message = "Password is required")
@MinLength(min = 12, message = "Password must be at least 12 characters")
@MustContainMin(min = 3, alphabet = ALPHA_LOWERCASE, message = "At least 3 lowercase letters")
@MustContainMin(min = 3, alphabet = ALPHA_UPPERCASE, message = "At least 3 uppercase letters")
@MustContainMin(min = 3, alphabet = NUMBER, message = "At least 3 numbers")
@MustContainMin(min = 3, alphabet = "@~_/", message = "At least 3 special characters")
@Compare(compareWith = "passwordConfirm", message = "Passwords do not match")
private String password;
private String passwordConfirm;
@Required(message = "Birth date is required")
@Date(format = "dd/MM/yyyy", message = "Invalid date format (dd/MM/yyyy)")
@MinAge(format = "dd/MM/yyyy", age = 18, message = "Must be at least 18 years old")
private String birthDate;
// Constructor
public UserRegistration(String email, String phone, String password,
String passwordConfirm, String birthDate) {
this.email = email;
this.phone = phone;
this.password = password;
this.passwordConfirm = passwordConfirm;
this.birthDate = birthDate;
}
// Getters and setters...
}
Annotations provide a clean, declarative way to define validation rules. However, they cannot express custom business logic - for that, use the Builder pattern with custom rules.