Creating a Custom Annotation

JSR 303 provides a set of built-in validations but there will be times when you need to create your own validation annotation. Thankfully the specification was written with that in mind and it is relatively simple to write a custom validation annotation.

A typical scenario could be that we want to validate that two passwords entered by the user match.

Defining the Annotation

We use the @interface keyword to define an annotation.

There are certain attributes and annotations we must define in order to satisfy the specification.

  • an attribute “message” that returns the default key for creating error messages in case the constraint is violated
  • an attribute “groups” that allows the specification of validation groups, to which this constraint belongs This must default to an empty array.
  • @Target({ METHOD, FIELD, ANNOTATION_TYPE }): Says, that type declarations and annotation declarations may be annotated with @Matches (but not method or field declarations e.g.)
  • @Retention(RUNTIME): Specifies, that annotations of this type will be available at runtime by the means of reflection
  • @Constraint(validatedBy = MatchesValidator.class): Specifies the validator to be used to validate elements annotated with @Matches
  • Documented: Says, that the use of @Matches will be contained in the JavaDoc of elements annotated with it

Implementing the ConstrainValidator

The next step is to implement the ConstraintValidator that will validate classes with the @Matches annotation

The ConstraintValidator interface specifies two type parameters, which we set in our implementation. The first specifies the annotation type to be validated by a ConstraintValidator (in our example Matches), the second the type of elements, which the validator can handle (we’re expecting an object). Specifying object means we don’t limit the validation annotation to a particular type. MVEL is used to inspect the properties of the object being validated. If we specified a particular type we could use the fields accessor methods. The implementation of the validator is straightforward. The initialize() method gives us access to any attributes of the annotation. In this case we’re expecting the name of the password field (field) and the match password field. (verifyfield)

The isValid() method determines whether the two fields match. If they do the @Matches annotation is satisfied. If they are not an exception is raised using the default message.

Note, that the specification recommends, that null values should be declared to be valid. If null is not a valid value for an element, it should be annotated with @NotNull explicitly.

Specifying the Error Message

Next we need to specify the error message. To do so, we create a file named under src/main/resources with the following content:

Using Our New Annotation

We can now use the @Matches annotation on our CreateUserAccountForm class to verify the password and passwordRepeat fields match.

Testing the New Constraint

We can then write a simple junit test to check our new constraint