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 two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

2 Responses to "Spring MVC Custom Validation Annotations"

  1. Sumit says:

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

  2. Akash says:

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

Leave a Reply


1 × seven =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

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

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close