You are currently viewing Spring Boot Custom Validation Annotation with Example
Spring Boot custom validation annotation

Spring Boot Custom Validation Annotation with Example

  • Post category:Java / Spring Boot
  • Post last modified:November 1, 2023
  • Reading time:5 mins read

In this tutorial, we’re going to create custom validation in Spring Boot so we can apply that to REST API and for other use cases, in a similar way you use other validation rules in Spring Boot projects.

In Spring Boot we can use some built-in validation constraints, such as the Jakarta Bean Validation to handle data validation. But sometimes the built-in validation might not have a validation constraint suitable for certain use cases. In that case, we can create our own custom validation annotation.

How to write custom annotations for validation in Spring Boot

We can create custom validation rules in Spring Boot by creating an annotation type (declared with @interface), specifying metadata including:

  • @Constraint – the annotation type constraint that implements the actual validation constraints/rules.
  • @Retention – tells when the annotation metadata can be accessed by the application.
  • @Target – indicate the contexts the annotation is applicable.

For example, let’s say we want to create a validation constraint that checks if the given value is numeric (this is especially useful for validating numeric string). Let’s call the constraint @Numeric annotation. We can begin to define the @Numeric thus:

@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = NumericValidator.class)
public @interface Numeric {
  ...
}

What is @interface?

We use the @interface to denote an annotation type definition. The @interface is different from the “normal” interface most of us are used to. You can learn more about the difference between interface and @interface from this StackOverflow question.

Example Custom Validation in Spring Boot

Now let’s see an example implementation of a custom validation constraint in Spring Boot; in this case, the @Numeric that checks if the input is a numeric (e.g. numeric string) or not.

Validation Constraint (ConstraintValidator)

First, we create the constraint – a class that implements ConstraintValidator interface, which handles the actual validation. Let’s call the constraint NumericValidator:

class NumericValidator implements ConstraintValidator<Numeric, Object> {
    @Override
    public void initialize(Numeric phoneNumber) {}

    @Override
    public boolean isValid(Object input, ConstraintValidatorContext cxt) {
        if (input == null) {
            return false;
        }

        if (input instanceof String) {
            // StringUtils is my helper class
            return StringUtils.isNumeric((String) input);
        }

        return input instanceof Number;
    }
}

The class overrides two methods:

  • initialize: This is where we can do initialisation stuff. This method gets called before the isValid.
  • isValid: in this method, we have the logic that checks if the input data meets the expectations. The goal of this method is to return a boolean value to indicate whether the validation passes (true) or not (false).

Custom Validation Annotation

Next, let’s create the annotation class that we can use to apply the validation constraint:

@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = NumericValidator.class)
@Documented
public @interface Numeric {
    String message() default "The value must be numeric";

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

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

Finally, to apply our new custom validator, we simply use the annotation @Numeric. For example:

@Numeric
String price

This Post Has One Comment

  1. Saravanan

    hi, i want to send that name variable as a parameter in this @Numeric , how?

Leave a Reply