Form validation with Spring MVC has not been my favourite part of web development up until now. However, today I was faced with the task yet again and decided to take a look at form validation with JSR-303 and the Hibernate Validator implementation. This article sums up my experiences this far.
As we will see, validation need not be tedious and consist of mile long Validator implementations once we embrace the power of annotations and JSR-303.
The article assumes that you have a correctly configured Spring application context set up with package scanning and annotation driven MVC:
<mvc:annotation-driven /> <context:component-scan base-package="com.codemunchies.jsr303" />
Maven dependencies
First off we need a couple of Maven dependencies in addition to the traditional Spring dependencies. The following three dependencies are added to your POM and you should be good to go:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.11</version>
</dependency>
You should also note that you need to add the JBoss Maven 2 repository to your POM in order for Maven to find the Hibernate Validator dependency.
Our view
We will need a simple view for our form, where the user can enter the information and submit it. We will use the Spring form taglib to construct our form and bind it to our form class, as shown in the code example below.
<p>
<form:form action="" method="post" commandName="userForm">
<div>
<form:label path="name">Name:</form:label>
<form:input path="name"/>
<form:errors path="name" />
</div>
<div>
<form:label path="email">Email:</form:label>
<form:input path="email" />
<form:errors path="email" />
</div>
<div>
<input type="submit" value=" OK "/>
</div>
</form:form>
</p>
Controller and form beans
Our controller consists of two methods, one for handling the initial GET request and one for handling the POST request upon form submission. The GET handler simply populates our model before we’re sent on our way to the form view.
@Controller
@RequestMapping("/form.html")
public class FormTestController {
@RequestMapping(method = RequestMethod.GET)
public String get(final ModelMap model) {
UserForm userForm = new UserForm();
model.addAttribute("userForm", userForm);
return "form";
}
@RequestMapping(method = RequestMethod.POST)
public String post(final ModelMap model, @Valid final UserForm userForm,
final BindingResult result) {
if (result.hasErrors()) {
return "form";
}
return "success";
}
}
The first interesting thing we see is in the POST handler method, where we have added a @Valid annotation to our form parameter. This indicates that we want this bean validated using the JSR-303 implementation, in this case Hibernate Validator. Whenever a POST request is sent to our controller the validator is invoked and our form bean is validated based on the annotated fields in our UserForm bean.
public class UserForm {
@NotEmpty
@Size(max = 20)
private String name;
@NotEmpty
@Email
private String email;
// Getters and setters
}
This tells the validator that none of the fields can be empty, that name should not be longer than 20 characters and that email should be a valid e-mail address (The @Email annotation is a Hibernate Validator annotation). Really quite neat!
With the above code, you will now get the following view by summiting an empty form:

Hibernate Validation provides default messages for the constraints, but these can easily be overridden.
As we continue to fill out the form with different values, we see the constraints kick into action and tells us to please fill out the form correctly:


Custom constraints
You can also create your own constraints quite easily. To demonstrate this we will add a list of web site addresses to our form, which will be represented by a list of Website objects in our UserForm.
public class Website {
@Url
private String url;
// Getters and setters
}
Which introduces the following change to our UserForm bean:
@Valid private List websites = new ArrayList();
The secret here is the custom @Url annotation which we have implemented below:
@Documented
@Constraint(validatedBy = UrlValidator.class)
@Target( { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Url {
public abstract String message() default "must start with 'http'";
public abstract Class[] groups() default {};
public abstract Class[] payload() default {};
}
The important part here is the abstract message() method which holds our default validation message. The UriValidator referenced in the @Constraint annotation is the class actually performing the validation, shown here:
public class UrlValidator implements ConstraintValidator<Url, String> {
@Override
public void initialize(final Url target) {
}
@Override
public boolean isValid(final String url,
final ConstraintValidatorContext context) {
return url.startsWith("http");
}
}
In our form view we add the following code to display the website input boxes:
<c:forEach var="website" varStatus="status" items="${userForm.websites}">
<c:set var="index" value="${status.index}"/>
<div>
Website ${index}:
<input id="websites${index}.url" name="websites[${index}].url" value="${userForm.websites[index].url}"/>
<form:errors path="websites[${index}].url" />
</div>
</c:forEach>
These changes results in the following view when we refresh our page and try to enter a web site address:

Entering valid information in all fields lets us pass through the POST handler method in our controller and we’re ready to move on with valid data!
Compared to the old Spring validator pattern which I’ve been using over the last years, this new way of validating forms is a breath of fresh air. Basic form validation is up and running in no time with a few annotations and you have the flexibility to perform more complex validation whenever the need arises. The only issue that I currently see (but which I have not thoroughly tested) is testing, which may be a bit more complicated due to the heavy use of annotations. But that is perhaps a topic for another article.
Sources of inspiration
Front page image by brian.gratwicke // http://www.flickr.com/photos/briangratwicke/2725343507/

Hi, great post. I have a question.
What is the sintax for the errors messaged keys in the resouce boundle?
{key} = Name must be provided
Thanks
Hi, Augustin.
The syntax for the error message keys is as follows:
Constraint.CommandName.FieldNameExample:
NotEmpty.userForm.nameHope this helps.
Jaran
Using the code provided, I couldn’t get it to work until I added the generics to the ConstraintValidator:
public class UrlValidator implements ConstraintValidator {
@Override
public void initialize(final Url target) {
}
@Override
public boolean isValid(final String url,
final ConstraintValidatorContext context) {
return url.startsWith("http");
}
}
@Chad You are absolutely right, and the generics were there when the post was created. It seems WordPress decided to remove them in the saving process
Fixed it now. Thanks for pointing it out!
How about using validation groups?
if errors presence, it goes back to same page. But i am loosing my drop down values. Should I set all the form data related fields (eg: drop down values, etc) again in the if block (if (results.hasErrors()) ??
great post. i also wanna share a spoon-fed tutorial on Hibernate integration to spring mvc 3.0 for newbies like me
http://www.adobocode.com/spring/adding-crud-capability-to-spring-mvc
hope this helps.
Hi Jaran
Thanks for taking the time to make this tutorial, it’s really appreciated.
I had tried to validation working in a more complex example and I had problems. So when I saw this minimalist example I thought I would succeed. Unfortunately, I still have the same error, so it would appear that I am doing something wrong.
When I do the GET on form.html the empty form appears as expected.
But when I submit the form (POST) it crashes without getting to the post method in FormTestController with this stack trace:
root cause
java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;
org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)
org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)
org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)
org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:764)
org.hibernate.validator.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:331)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForRedefinedDefaultGroup(ValidatorImpl.java:278)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:260)
org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:213)
org.hibernate.validator.engine.ValidatorImpl.validate(ValidatorImpl.java:119)
org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:74)
org.springframework.validation.DataBinder.validate(DataBinder.java:692)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doBind(HandlerMethodInvoker.java:797)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:349)
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:169)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:421)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:409)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:771)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
If I remove the @Valid from the post method signature I can see it execute in the debugger but of course there is no validation.
So, I am doing something wrong.
My pom dependencies look like this:
org.hibernate
hibernate-validator
4.0.2.GA
slf4j-api
org.slf4j
org.springframework
spring-context
3.0.2.RELEASE
org.springframework
spring-beans
3.0.2.RELEASE
org.springframework
spring-webmvc
3.0.2.RELEASE
org.springframework
spring-web
3.0.2.RELEASE
org.hibernate
hibernate-entitymanager
3.3.2.GA
org.slf4j
slf4j-api
1.5.11
org.slf4j
slf4j-log4j12
1.5.11
javax.servlet
jstl
1.1.2
Any ideas?
There’s not much here to go wrong baring a problem with dependencies…
Any help would really be much appreciated. Although new to Spring I have a lot of experience with Java web but I really like the elegance of Spring.
Regards
Lyndon
@Lyndon: It’s difficult to see what’s wrong from your stack trace. Could you please use pastebin or similar to post the code that is failing?
hi.
its a good article but i want to know can i validate to fields like as username and password without having any bean related to those fields?
for example is there any annotation or another way that i check my fields values in related method?
i add the list to the page but there is a exception as bleow! do you know why?
Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property ‘websites[0]‘ of bean class [net.roseindia.form.UserForm]: Index of out of bounds in property path ‘websites[0]‘; nested exception is java.lang.IndexOutOfBoundsException: Index: 0, Size: 0