Home » Java » Enterprise Java » Spring MVC Custom Validation Annotations

About Steve Hanson

Steve is a software developer interested in web development and new technologies. He currently works as a Java consultant at Credera in Dallas, TX.

Spring MVC Custom Validation Annotations

Last tutorial, I showed how to validate a form using annotations. This works great for simple validations, but eventually, you’ll need to validate some custom rules that aren’t available in the out-of-the-box annotations. For example, what if you need to validate that a user is over 21 years old, calculated based off their input birthdate, or, maybe you need to validate that the user’s phone area code is in Nebraska, USA. This tutorial with full source code will show how to create custom validation annotations that you can use along-side the JSR-303 and Hibernate Validator annotations we explored in the last tutorial.

You can grab the code for this tutorial on GitHub if you want to follow along.

For this example, let’s say we have a form with a phone number field and a birthdate field, and we want to validate the the phone number is valid (simple check for format) and that the user was born in 1989. There are no out-of-the-box annotations that support these (as far as I know), so we will write custom validation annotations which we can then re-use, just like the built-in JSR-303 ones.

When we are done, we will apply our annotations to our form object, like so:

public class Subscriber {

    ...

	@Phone
	private String phone;

	@Year(1989)
	private Date birthday;

    // getters setters ...

}

Let’s get started with the @Phone annotation. We will be creating two classes: Phone, which is the annotation, and PhoneConstraintValidator which contains the validation logic. The first step is to create the Phone annotation class:

@Documented
@Constraint(validatedBy = PhoneConstraintValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {

	String message() default "{Phone}";

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

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

}

The code above is mostly just boiler-plate. The three methods in the annotation are required by the JSR-303 spec. If our annotation accepted any arguments, we would have defined them there as methods. We will see this in our next annotation later in this tutorial. The most important part of the class above is the @Constraint annotation on the class which specifies that we will use our PhoneConstraintValidator class for the validation logic. The message() method defines how the message is resolved. By specifying “{Phone}”, we can override the message in a Spring resource bundle using the Phone key (see my other validation tutorial for details about messages).

Now, we define the constraint validator:

public class PhoneConstraintValidator implements ConstraintValidator<Phone, String> {

	@Override
	public void initialize(Phone phone) { }

	@Override
	public boolean isValid(String phoneField, ConstraintValidatorContext cxt) {
		if(phoneField == null) {
			return false;
		}
		return phoneField.matches("[0-9()-\.]*");
	}

}

Let’s look at the above code. The templated type of the superclass takes two types: the type of the annotation it supports, and the type of the property it validates (in this example, Phone, String).

The “initialize” method is empty here, but it can be used to save data from the annotation, as we will see below when we define our other annotation.

Finally, the actual logic happens in the “isValid” method. The field value is passed in as the first argument, and we do our validation here. As you can see, I am just validating that the phone number only contains numbers, parentheses or dashes.

That’s it for this annotation! The annotation can now be used on a field as shown above on our form object.

Now, let’s do our second annotation. This one is a little contrived – we will validate that the user’s birthdate is in 1989. In the future, we may need to validate dates are in other years, though, so rather than create an annotation that validates the year to be 1989, we will let it take an argument to specify the year to validate against. Example usage:

@Year(1989)
private Date birthDate;

Now, the annotation:

@Documented
@Constraint(validatedBy = YearConstraintValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Year {

	int value();

	String message() default "{Year}";

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

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

}

Notice the “value()” method. This exposes the “value” argument of the annotation which we will use to pass the year that the annotation should validate against. The rest of the code is mostly boilerplate

Now, the constraint validator:

public class YearConstraintValidator implements ConstraintValidator<Year, Date> {

	private int annotationYear;

	@Override
	public void initialize(Year year) {
		this.annotationYear = year.value();
	}

	@Override
	public boolean isValid(Date target, ConstraintValidatorContext cxt) {
		if(target == null) {
			return true;
		}
		Calendar c = Calendar.getInstance();
		c.setTime(target);
		int fieldYear = c.get(Calendar.YEAR);
		return fieldYear == annotationYear;
	}

}

The first thing to notice, is that, this time, we are saving the year passed into the annotation as a member variable of the constraint validator class. This allows us to access the value in our “isValid” method.

The isValid method is pretty straightforward code wrestling with the obnoxious Date/Calendar API’s to validate that the value of the annotated field matches the year that the validation annotation specified (I may post an example using JodaTime sometime if I get around to it). And now, if we start up our web application, our two validations are in place and ready to be used!

custom-validation-example-e1372733063855

That’s all. Did I miss anything? Have questions? Let me know in the comments.

Full Source: ZIPGitHub
To run the code from this tutorial: Must have Gradle installed. Clone the GitHub repo or download the ZIP and extract. Open command prompt to code location. Run gradle jettyRunWar. Navigate in browser to http://localhost:8080.
 

Reference: Spring MVC Custom Validation Annotations from our JCG partner Steve Hanson at the CodeTutr blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

and many more ....

 

2 comments

  1. Nice post.
    How can i add additional attributes to my custom annotation ?
    For ex Year(day=””, month=””)

  2. Can you send me simple explained example of spring validation without hibernate?

Leave a Reply

Your email address will not be published. Required fields are marked *

*


6 × six =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Want to take your Java Skills to the next level?
Grab our programming books for FREE!
  • Save time by leveraging our field-tested solutions to common problems.
  • The books cover a wide range of topics, from JPA and JUnit, to JMeter and Android.
  • Each book comes as a standalone guide (with source code provided), so that you use it as reference.
Last Step ...

Where should we send the free eBooks?

Good Work!
To download the books, please verify your email address by following the instructions found on the email we just sent you.