Spring MVC annotation validation with jsr 303 + Custom validation messages in properties file

 

                            NOTE: Source Code available for download at the bottom of the page

 
 
What are we going to learn?
   We will develop a Spring Web MVC based web application for employee enrollment. We will use Spring MVC Validation (jsr 303) using annotations for validating the enrollment form. 
 
What is JSR 303?
   JSR 303 is a framework for bean validation. Bean Validation defines a metadata model and API for JavaBean validation. JSR 303 provides us the feasibility to define the validations using constraint annotations on the model. Thus providing the coupling between the model attributes and the constraints. As defined, JSR 303 is an API and there are implementations available for it. We will be using implementation provided by hibernate called "Hibernate-Validator" for our Spring MVC validation.
 
For Example : 
@NotNull(message="Field Required!") @NumberFormat(style = Style.NUMBER) @Min(1)
private Long employeeId;
 
The above code defines employeeId which shouldn't be null and Min declares the minimum value for the field. thus coupling validations and model attributes using annotations. 
 
What's needed?
  • Maven
  • JDK 1.5 or higher
  • eclipse 
  • Tomcat
Overall design
  • We will create a employee enrollment page which will capture the required fields.
  • We will add validation logic to the form using Spring MVC validation annotations using hibernate-validator which is an implementation of JSR 303.
  • A successful page if validation is successful.
 
Let's get started.
 
End Output:
 
Spring MVC Employee Enrollment page 

 
 
 
 
Setup Eclipse Project
 
Create a dynamic web project using maven artifact "maven-archetype-webapp" as shown HERE
 
 
Following Steps summarize the process for creating the Spring MVC project.
  1. Update POM file to get dependencies for Spring Web MVC.
  2. Add declaration for Spring DispatcherServler servlet in web.xml.
  3. Create Web Application Context file + Define Error property file as message resource in the Context file.
  4. Create Model + Add constraint/validation.
  5. Create Controller.
  6. Create JSP Page.
 
Step 1: Update POM file to get dependencies for Spring Web MVC
 
For Spring Web MVC jars + JSR 303 Implementation "hibernate-validator" + validation API
 
pom.xml
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.0.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.1.0.CR1</version>
		</dependency>
 
<artifactId>spring-webmvc</artifactId> gets all the Spring jars needed for Spring Web MVC


<artifactId>validation-api</artifactId> API for bean validation

<artifactId>hibernate-validator</artifactId> hibernate validator implementation of JSR 303
 
 
 
Step 2: Add declaration for Spring DispatcherServler servlet in web.xml.
 
web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Employee Project SPRING WEB MVC</display-name>
  
  <servlet>
   <servlet-name>Employee</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
   	<param-name>contextConfigLocation</param-name>
    	<param-value>
        	 /WEB-INF/employee-servlet.xml
        </param-value>
   </init-param>
</servlet>

<servlet-mapping>
   <servlet-name>Employee</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>
  
</web-app>
 
 
<servlet-mapping> for Employee forwards all the requested url's to servlet named Employee
	
<servlet> declaration for Employee is defined as Spring DispatcherServlet. Upon initialization of a DispatcherServlet, 
Spring MVC looks for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and createsthe beans defined there, overriding the definitions of any beans defined with the same name in the global scope.

If you want to name the context file with name different than [servlet-name]-servlet.xml , you can use  
		
		<init-param>
		
		<param-name>contextConfigLocation</param-name>
		
		<param-value>
		
		/WEB-INF/employee-servlet.xml
		
		</param-value>
		
		</init-param>
			
 to define the location and file name.
 
 
Step 3: Create Web Application Context file.
 
employee-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:annotation-config />
	<context:component-scan base-package="com.visionjava" />
	<mvc:annotation-driven />

	<bean id="viewResolver"	class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="cache" value="true" />
		<property name="prefix" value="/WEB-INF/jspPages/" />
		<property name="suffix" value=".jsp" />
		<property name="redirectHttp10Compatible" value="false" />
	</bean>
	
	 <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
                <property name="basenames">
                 <list>
                   <value>classpath:/com/visionjava/employee/error</value>
                 </list>
               </property>
               <property name="cacheSeconds" value="3600"/>
         </bean>	

</beans>

<context:annotation-config /> is used to activate annotations in beans already registered in the application context. Ex: @Autowired, @Resource, @Required, @PostConstruct

<context:component-scan base-package="com.visionjava" /> scans the defined packages and registers the beans with application context(like controller, bean).


<mvc:annotation-driven /> declares explicit support for new Spring MVC features like @Valid, @RequestBody, @RequestResponse etc.


<bean id="viewResolver"	class="org.springframework.web.servlet.view.InternalResourceViewResolver"> provides access to controller for the jsp pages under WEB-INF folder. It's a good practice to put all the jsp pages under WEB-INF folder to avoid giving direct access.


Lets spend time examining the messageSource id we declared.
We use Spring's ReloadableResourceBundleMessageSource to load our message resource files. Using propert "basenames" we provide the list of all our message resource files. ReloadableResourceBundleMessageSource gives us the feasibility to define time in milliseconds when the file is polled and any changes are loaded without redeploying the application.




error.properties 
typeMismatch.employee.dateOfBirth=Entered Date is not a vlaid format!
Pattern.employee.placeofBirth=Field accepts characters only!
 
 
 
 
Step 4: Create Model + Add constraint/validation.
 
Employee.java
package com.visionjava.employee.model;

import java.io.Serializable;
import java.util.Date;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import org.springframework.format.annotation.NumberFormat.Style;

public class Employee implements Serializable {
	
	private static final long serialVersionUID = 1L;

	@NotNull(message="Field Required!") @NumberFormat(style = Style.NUMBER) @Min(1)
	private Long employeeId;
	
	@NotNull(message="Field Required!") @Size(min=1,max=10) 
	private String firstName;
	
	@NotNull(message="Field Required!") @Size(min=1,max=10)
	private String lastName;
	
	@NotNull(message="Field Required!")
	@DateTimeFormat(pattern = "MM/dd/yyyy")
	private Date dateOfBirth;
	
	@NotNull(message="Field Required!")
	@Pattern(regexp="^[A-Za-z]*$")
	private String placeofBirth;
	
	
	
	public Long getEmployeeId() {
		return employeeId;
	}
	public void setEmployeeId(Long employeeId) {
		this.employeeId = employeeId;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public Date getDateOfBirth() {
		return dateOfBirth;
	}
	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}
	public String getPlaceofBirth() {
		return placeofBirth;
	}
	public void setPlaceofBirth(String placeofBirth) {
		this.placeofBirth = placeofBirth;
	}
	

}
 
Step 5: Create Controller
 
EmployeeController.java
package com.visionjava.employee.controller;

import javax.validation.Valid;

import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.visionjava.employee.model.Employee;

@Controller
public class EmployeeController {
	
	@InitBinder
	public void allowEmptyDateBinding( WebDataBinder binder )
	{
	    // tell spring to set empty values as null instead of empty string.
	    binder.registerCustomEditor( String.class, new StringTrimmerEditor( true ));
	   
	}

	@RequestMapping("/")
	public String showHomePage(){		
		return "employeeHomePage";
	}
	
	@RequestMapping(value="/enroll", method = RequestMethod.GET)
	public ModelAndView showEnrollmentPage(){		
		ModelAndView modelAndView = new ModelAndView("employeeEnrollment");
		modelAndView.addObject("employee", new Employee());
		return modelAndView;
	}
	
	@RequestMapping(value="/submitEnrollment", method = RequestMethod.POST)
	public String submitEnrollment(@Valid @ModelAttribute("employee") Employee employee, 
									BindingResult bindingResult, ModelMap modelMap ){		
		
		if(bindingResult.hasErrors()){
			return "employeeEnrollment";
		}
		
		return "employeeHomePage";
	}
	
}
 
@Controller -- declares the class as Spring Controller type class.


@RequestMapping("/")-- Annotation for mapping web requests onto specific handler classes and/or handler methods.In our case the homepage request is handled by this controller.
The return value is resolved by the view resolver which appends the location and suffix for the string.

	
Step 6: Create JSP Page
 
employeeHomePage.jsp
										<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Employee Enrollment Form</title>
</head>
<body>
	
		<h1>Employee Enrollment Form</h1>
	
<form:form name="employee" id="enrollmentForm" action="submitEnrollment" commandName="employee">
	<table cellspacing="0" cellpadding="0" border="0" width="718">
		<tr>
			<td>Employee ID:</td>
			<td><form:input path="employeeId" /> <form:errors path="employeeId" cssStyle="color:red"/></td>
		</tr>
		<tr>
			<td colspan="2">&nbsp;</td>
		</tr>
		<tr>
			<td>Employee First Name:</td>
			<td><form:input path="firstName" /> <form:errors
					path="firstName" cssStyle="color:red"/></td>
		</tr>
		<tr>
			<td colspan="2">&nbsp;</td>
		</tr>
		<tr>
			<td>Employee Last Name:</td>
			<td><form:input path="lastName" /> <form:errors path="lastName" cssStyle="color:red"/></td>
		</tr>
		<tr>
			<td colspan="2">&nbsp;</td>
		</tr>
		<tr>
			<td>Employee Date Of Birth:</td>
			<td><form:input path="dateOfBirth" /> <form:errors
					path="dateOfBirth" cssStyle="color:red"/></td>
		</tr>
		<tr>
			<td colspan="2">&nbsp;</td>
		</tr>
		<tr>
			<td>Place Of Birth:</td>
			<td><form:input path="placeofBirth" /> <form:errors
					path="placeofBirth" cssStyle="color:red"/></td>
		</tr>
		<tr>
			<td colspan="2">&nbsp;</td>
		</tr>
		<tr>
		<td colspan="2" align="center">
		<input type="submit" id="submitButton" name="enrollmentSubmit" value="Submit Enrollment"/>
	</table>
</form:form>



</body>
</html>
 
Project Structure:
 
 
 
 
 
 
Source Code : Employee.rar
Web Analytics