Close

Java Bean Validation - Creating custom constraint annotation

[Last Updated: Aug 25, 2021]

JavaBean validation specifications provides ways to hook up custom annotations to the framework.

In cases where predefined constraints are not sufficient, we can easily create custom constraints for our specific validation requirements.

We have to follow these steps to create a custom constraint:

  1. Create a validator class which should implement javax.validation.ConstraintValidator interface.
  2. Create the constraint annotation which should be annotated with javax.validation.Constraint annotation.

To understand how to achieve above two steps, let's see the definitions of ConstraintValidator interface and javax.validation.Constraint annotation.


Definition of ConstraintValidator

(Version: java-bean-validation 2.0.2)
package javax.validation;
   ........
public interface ConstraintValidator<A extends Annotation, T> {
    default void initialize(A constraintAnnotation) {} 1
    boolean isValid(T value, ConstraintValidatorContext context); 2
}
1The default implementation is a no-op. We can override it to initialize instance variables of the validator. The constraint annotation for a given constraint declaration is passed. This method is guaranteed to be called before any use of this instance for validation.
2The implementation of this method should perform validation. This method should not alter the state of the validator. This method can be accessed concurrently, thread-safety must be ensured by the implementation.
parameters:
value object to validate.
context provides contextual data and operation when applying a given constraint validator
returns: false if value does not pass the constraint.

Definition of Constraint

(Version: java-bean-validation 2.0.2)
package javax.validation;
   ........
@Documented
@Target({ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface Constraint {
    Class<? extends ConstraintValidator<?, ?>>[] validatedBy(); 1
}
1The element validatedBy is used to tie the validators (via an array of ConstraintValidator classes) to be executed for this constraint annotation. Multiple ConstraintValidator can be specified when each validator validates different targets. These targets are specified by SupportedValidationTarget#ValidationTarget. SupportedValidationTarget annotation is used on ConstraintValidator. (tutorial on SupportedValidationTarget).

Example


Creating the custom annotation

In this example, we are going to validate weather a provided string is a valid language name or not.

New constraint annotation

package com.logicbig.example;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
        ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = LanguageValidator.class)
@Documented
public @interface Language {
    String message() default "must be a valid language display name." +
            " found: ${validatedValue}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Creating the validator:

package com.logicbig.example;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Locale;

public class LanguageValidator implements ConstraintValidator<Language, String> {

    @Override
    public void initialize(Language constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        for (Locale locale : Locale.getAvailableLocales()) {
            if (locale.getDisplayLanguage().equalsIgnoreCase(value)) {
                return true;
            }
        }

        return false;
    }
}


The bean using our new constraint annotation

package com.logicbig.example;

class TestBean {
    @Language
    private String language;

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }
}


Performing validation

package com.logicbig.example;

import javax.validation.*;

public class CustomConstraintExample {
    private static final Validator validator;

    static {
        Configuration<?> config = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = config.buildValidatorFactory();
        validator = factory.getValidator();
        factory.close();
    }

    public static void main(String[] args) {
        TestBean testBean = new TestBean();
        testBean.setLanguage("englis");
        validator.validate(testBean).stream().forEach(CustomConstraintExample::printError);
    }

    private static void printError(ConstraintViolation<TestBean> violation) {
        System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
    }
}

Output

language must be a valid language display name. found: englis

Example Project

Dependencies and Technologies Used:

  • hibernate-validator 6.2.0.Final (Hibernate's Jakarta Bean Validation reference implementation)
     Version Compatibility: 5.0.0.Final - 6.2.0.Final Version List
    ×

    Version compatibilities of hibernate-validator with this example:

      groupId: org.hibernate
      artifactId: hibernate-validator
      Reference implementation for Bean Validation 1.1
    • 5.0.0.Final
    • 5.0.1.Final
    • 5.0.2.Final
    • 5.0.3.Final
    • 5.1.0.Final
    • 5.1.1.Final
    • 5.1.2.Final
    • 5.1.3.Final
    • 5.2.0.Final
    • 5.2.1.Final
    • 5.2.2.Final
    • 5.2.3.Final
    • 5.2.4.Final
    • 5.2.5.Final
    • 5.3.0.Final
    • 5.3.1.Final
    • 5.3.2.Final
    • 5.3.3.Final
    • 5.3.4.Final
    • 5.3.5.Final
    • 5.3.6.Final
    • 5.4.0.Final
    • 5.4.1.Final
    • 5.4.2.Final
    • 5.4.3.Final
    • groupId: org.hibernate.validator
      artifactId: hibernate-validator
      Reference implementation for Bean Validation 2.0
    • 6.0.0.Final
    • 6.0.1.Final
    • 6.0.2.Final
    • 6.0.3.Final
    • 6.0.4.Final
    • 6.0.5.Final
    • 6.0.6.Final
    • 6.0.7.Final
    • 6.0.8.Final
    • 6.0.9.Final
    • 6.0.10.Final
    • 6.0.11.Final
    • 6.0.12.Final
    • 6.0.13.Final
    • 6.0.14.Final
    • 6.0.15.Final
    • 6.0.16.Final
    • 6.0.17.Final
    • 6.0.18.Final
    • 6.0.19.Final
    • 6.0.20.Final
    • 6.0.21.Final
    • 6.0.22.Final
    • 6.1.0.Final
    • 6.1.1.Final
    • 6.1.2.Final
    • 6.1.3.Final
    • 6.1.4.Final
    • 6.1.5.Final
    • 6.1.6.Final
    • 6.1.7.Final
    • 6.2.0.Final
    • Version 7 and later:
      Jakarta Bean Validation 3.0
      jakarta.* packages

    Versions in green have been tested.

  • javax.el-api 3.0.0 (Expression Language 3.0 API)
  • javax.el 2.2.6 (Expression Language 2.2 Implementation)
  • JDK 8
  • Maven 3.8.1

Java Bean Validation - Creating Custom Constraint Select All Download
  • validation-custom-constraint
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • LanguageValidator.java

    See Also