重情义的八宝粥 · JSON schema form does ...· 7 小时前 · |
单身的麦片 · 中际旭创:公司2022年高速光模块销量达61 ...· 1 月前 · |
很拉风的沙滩裤 · Excel VBA与VSTO基础实战指南 ...· 1 月前 · |
长情的台灯 · 战锤40K:星际战士2发售时间、价格、中文、 ...· 2 月前 · |
爱运动的皮蛋 · ISO_DATE_TIME_OFFSET· 4 月前 · |
安静的小摩托 · 如何在XAML中使用触发器触发C#中的方法_ ...· 4 月前 · |
按钮 input email form |
https://terasolunaorg.github.io/guideline/5.2.0.RELEASE/en/ArchitectureInDetail/WebApplicationDetail/Validation.html |
独立的红豆
7 月前 |
It is mandatory to check whether the value entered by the user is correct. Validation of input value is broadly classified into
1. is an example of mandatory check and number of digits check and 2. is an example of check of whether EMail is registered and whether order count is within the count of the available stock.
In this section, 1. is explained and this check is called “Input validation”. 2. is called “Business logic check”. For business logic check, refer to Domain Layer Implementation .
In this guideline, validation check should be performed in application layer whereas business logic check should be performed in domain layer.
Input validation of Web application is performed at server side and client side (JavaScript). It is mandatory to check at the Server Side. However, if the same check is performed at the client side also, usability improves since validation results can be analyzed without communicating with the server.
Warning
Input validation should be performed at the server side as the process at client side may be altered using JavaScript. If the validation is performed only at the client side without performing at the server side, the system may be exposed to danger.
Input validation at client side will be explained later. Only input validation at the server side is mentioned in the first version.
Input validation is classified into single item check and correlation item check.
Spring supports Bean Validation which is a Java standard.
This Bean Validation is used for single item check.
Bean Validation or
org.springframework.validation.Validator
interface provided by Spring is used for correlation item check.
When Bean validation 1.1 (Hibernate Validator 5.x) or higher version is to be used,
in addition to jar file of Hibernate Validator and jar file storing API specifications class (
javax.validation
package class) of Bean Validation,
libraries that store the following classes are required.
javax.el
package class)
If the file is run by deploying on application server, dependent libraries need not be added; since these libraries are provided by application server. However, when it is run in standalone environment (JUnit etc.), these libraries need to be added as dependent libraries.
An example of adding libraries which are required when running Bean Validation 1.1 or higher version in standalone environment is given below.
<!-- (1) -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<scope>test</scope> <!-- (2) -->
</dependency>
Add a library wherein a class for Expression Language is stored,
in pom.xml
file of the project to be run in standalone environment.
In the above example, libraries provided for Apache Tomcat to be embedded are specified.
API specifications classes of Expression Language and reference implementation classes are both stored in jar file of tomcat-embed-el
.
When dependent libraries are required to execute JUnit, an appropriate scope is test
.
In the above example of settings, it is a prerequisite that version of dependent libraries should be stored in the parent project.
Therefore, <version>
element is not specified.
4.1.2.2. Single item check¶
For the implementation of single item check,
- Bean Validation annotation should be assigned to the field of form class
@Validated
annotation should be assigned in Controller for validation
- Tag for displaying validation error message should be added to JSP
<mvc:annotation-driven>
settings are carried out in spring-mvc.xml, Bean Validation is enabled.
4.1.2.2.1. Basic single item check¶
Implementation method is explained using “New user registration” process as an example. Rules for checking “New user registration” form are provided below.
Form class
Assign Bean Validation annotation to each field of form class.
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull // (1)
@Size(min = 1, max = 20) // (2)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email // (3)
private String email;
@NotNull // (4)
@Min(0) // (5)
@Max(200) // (6)
private Integer age;
// omitted setter/getter
Assign javax.validation.constraints.NotNull
indicating that the target field is not null
.
In Spring MVC, when form is sent with input fields left blank,
empty string instead of null bindsto form object by default.
This @NotNull
checks that name
exists as request parameter.
Assign javax.validation.constraints.Size
indicating that the string length (or collection size) of the target field is within the specified size range.
Since empty string binds to the field where string is left blank by default in Spring MVC,
‘1 or more character’ rule indicates Mandatory input.
Assign org.hibernate.validator.constraints.Email
indicating that the target field is in RFC2822-compliant E-mail format.
When E-mail format requirements are flexible than RFC2822-compliant constraints, regular expression should be specified using javax.validation.constraints.Pattern
instead of @Email
.
Refer to Bean Validation check rules and Hibernate Validator check rules for standard annotations of Bean Validation and annotations provided by Hibernate.
Refer to Binding null to blank string field for the method of binding null
when input field is left blank.
Controller class
Assign @Validated
to form class for input validation.
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
return new UserForm();
@RequestMapping(value = "create", method = RequestMethod.GET, params = "form")
public String createForm() {
return "user/createForm"; // (1)
@RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm")
public String createConfirm(@Validated /* (2) */ UserForm form, BindingResult /* (3) */ result) {
if (result.hasErrors()) { // (4)
return "user/createForm";
return "user/createConfirm";
@RequestMapping(value = "create", method = RequestMethod.POST)
public String create(@Validated UserForm form, BindingResult result) { // (5)
if (result.hasErrors()) {
return "user/createForm";
// omitted business logic
return "redirect:/user/create?complete";
@RequestMapping(value = "create", method = RequestMethod.GET, params = "complete")
public String createComplete() {
return "user/createComplete";
Add org.springframework.validation.BindingResult
that stores check result of input validation performed in step (2).
This BindingResult
must be specified immediately after the form argument.
When not specified immediately, org.springframework.validation.BindException
is thrown.
Input validation should be executed againeven at the time of submission on the confirmation screen.
There is a possibility of data tempering and hence Input validation must be performed just before entering business logic.
@Validated
is not a standard Bean Validation annotation. It is an independent annotation provided by Spring.
Bean Validation standard javax.validation.Valid
annotation can also be used. However, @Validated
is better as compared to @Valid
annotation.
Validation group can be specified in case of @Validated
and hence @Validated
is recommended in this guideline.
<%-- WEB-INF/views/user/createForm.jsp --%>
<form:form modelAttribute="userForm" method="post"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name">Name:</form:label>
<form:input path="name" />
<form:errors path="name" /><%--(1) --%>
<form:label path="email">Email:</form:label>
<form:input path="email" />
<form:errors path="email" />
<form:label path="age">Age:</form:label>
<form:input path="age" />
<form:errors path="age" />
<form:button name="confirm">Confirm</form:button>
</form:form>
</body>
</html>
In Bean Validation, null
is a valid input value except the following annotations.
javax.validation.constraints.NotNull
org.hibernate.validator.constraints.NotEmpty
org.hibernate.validator.constraints.NotBlank
In the above example, error messages related to @Min
and @Max
annotations are not displayed. This is because null
is a valid value for @Min
and @Max
annotations.
Next, send the form by entering any value in the field.
Error message is not displayed since input value of Name fulfills validation conditions.
Error message is displayed since input value of Email is not in Email format though it fulfills the conditions related to string length.
Error message is displayed since input value of Age exceeds maximum value.
Change the form as follows to change the style at the time of error.
<form:form modelAttribute="userForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name" cssErrorClass="error-label">Name:</form:label><%-- (1) --%>
<form:input path="name" cssErrorClass="error-input" /><%-- (2) --%>
<form:errors path="name" cssClass="error-messages" /><%-- (3) --%>
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<form:errors path="email" cssClass="error-messages" />
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<form:errors path="age" cssClass="error-messages" />
<form:button name="confirm">Confirm</form:button>
</form:form>
For example, if the following CSS is applied to this JSP, error screen is displayed as follows.
.form-horizontal input {
display: block;
float: left;
.form-horizontal label {
display: block;
float: left;
text-align: right;
float: left;
.form-horizontal br {
clear: left;
.error-label {
color: #b94a48;
.error-input {
border-color: #b94a48;
margin-left: 5px;
.error-messages {
color: #b94a48;
display: block;
padding-left: 5px;
overflow-x: auto;
CSS can be customized as per the requirements of screen.
Instead of displaying the error messages next to each input field,
output them collectively.
<form:form modelAttribute="userForm" method="post"
action="${pageContext.request.contextPath}/user/create">
<form:errors path="*" element="div" cssClass="error-message-list" /><%-- (1) --%>
<form:label path="name" cssErrorClass="error-label">Name:</form:label>
<form:input path="name" cssErrorClass="error-input" />
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<form:button name="confirm">Confirm</form:button>
</form:form>
By specifying *
in path
attribute of <form:errors>
in <form:form>
tag,
all error messages related to Model specified in modelAttribute
attribute of <form:form>
can be output.
Tag name including these error messages can be specified in element
attribute. By default, it is span
. However,
specify div
to output error message list as block element.
Specify CSS class in cssClass
attribute.
An example of error message is shown when the following CSS class is applied.
.form-horizontal input {
display: block;
float: left;
.form-horizontal label {
display: block;
float: left;
text-align: right;
float: left;
.form-horizontal br {
clear: left;
.error-label {
color: #b94a48;
.error-input {
border-color: #b94a48;
margin-left: 5px;
.error-message-list {
color: #b94a48;
padding:5px 10px;
background-color: #fde9f3;
border:1px solid #c98186;
border-radius:5px;
margin-bottom: 10px;
By default, field name is not included in error message, hence it is difficult to understand which error message corresponds to which field.
Therefore, when an error message is to be displayed in a list, it is necessary to define the message such that field name is included in the error message.
For method of defining error messages, refer to “Definition of error messages”.
Points to be noted when displaying error messages in a list
Error messages are output in a random order and the output order cannot be controlled by standard function.
Therefore, when an output order needs to be controlled (to be kept constant), an extended implementation such as sorting the error information, etc. is required.
In the method of “Displaying error messages in a list”,
- Error message definition in feed unit
- Extended implementation to control the output order of error messages
are required. Therefore, the cost is higher as compared to “displaying error messages next to input field”.
This guideline recommends the method of “displaying error messages next to input field” when there are no constraints due to screen requirements.
Further, following method can be considered as extended methods to control output order of error message.
Creating inherited class of org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
provided by Spring Framework,
and sorting error information by overriding processConstraintViolations
method, etc.
About @GroupSequence annotation
A mechanism of @GroupSequence annotation is provided to control the check sequence;
however, add a note that this mechanism is not to control the output order of error message as operations given below are performed.
- When an error occurs, checking for subsequent groups is not executed.
- If multiple errors (errors in multiple fields) occur in the check of identical groups, then the output order of error messages would be random.
Use <spring:nestedPath>
tag to display error messages collectively outside the <form:form>
tag.
<spring:nestedPath path="userForm">
<form:errors path="*" element="div"
cssClass="error-message-list" />
</spring:nestedPath>
<form:form modelAttribute="userForm" method="post"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name" cssErrorClass="error-label">Name:</form:label>
<form:input path="name" cssErrorClass="error-input" />
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<form:button name="confirm">Confirm</form:button>
</form:form>
4.1.2.2.2. Date and time format check¶
In case of performing the date and time format check, it is recommend the use of @DateTimeFormat
annotation offered by Spring rather than the use of Bean Validation mechanism.
About how to use the @DateTimeFormat
annotation, refer Date and time format conversion of fields.
It is also possible to check the date and time format using @Pattern
annotation of the Bean Validation.
However, it is necessary to write the date and time format in regular expressions while using @Pattern
annotation. If you want to check the date and time that does not exist, the description is complicated.
Therefore @DateTimeFormat
annotation is more simpler than the @Pattern
annotation.
Since @DateTimeFormat
annotation is one of the type conversion mechanism provided by Spring, instead of the error messages of Bean Validation, the error message of the type mismatch exception (TypeMismatchException
) has been displayed on screen at the time of input error.
In order to avoid the actual exception message gets displayed on the screen, it is necessary to configure the error message in the property file which will be displayed at the time of type mismatch is occurred.
For more detail, refer Type mismatch.
4.1.2.2.3. Single item check of nested Bean¶
The method to validate nested Bean using Bean Validation is explained below.
“Ordering” process of an EC site is considered as an example. Rules for checking “Order” form are provided below.
Use the same form class since receiverAddress
and senderAddress
are objects of the same class.
Form class
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class OrderForm implements Serializable {
private static final long serialVersionUID = 1L;
@Size(max = 5)
@Pattern(regexp = "[a-zA-Z0-9]*")
private String coupon;
@NotNull // (1)
@Valid // (2)
private AddressForm receiverAddress;
@NotNull
@Valid
private AddressForm senderAddress;
// omitted setter/getter
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class AddressForm implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(min = 1, max = 50)
private String name;
@NotNull
@Size(min = 1, max = 10)
private String postcode;
@NotNull
@Size(min = 1, max = 100)
private String address;
// omitted setter/getter
Controller class
It is not different from the Controller described earlier.
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping("order")
@Controller
public class OrderController {
@ModelAttribute
public OrderForm setupForm() {
return new OrderForm();
@RequestMapping(value = "order", method = RequestMethod.GET, params = "form")
public String orderForm() {
return "order/orderForm";
@RequestMapping(value = "order", method = RequestMethod.POST, params = "confirm")
public String orderConfirm(@Validated OrderForm form, BindingResult result) {
if (result.hasErrors()) {
return "order/orderForm";
return "order/orderConfirm";
<!DOCTYPE html>
<%-- WEB-INF/views/order/orderForm.jsp --%>
<style type="text/css">
/* omitted (same as previous sample) */
</style>
</head>
<form:form modelAttribute="orderForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/order/order">
<form:label path="coupon" cssErrorClass="error-label">Coupon Code:</form:label>
<form:input path="coupon" cssErrorClass="error-input" />
<form:errors path="coupon" cssClass="error-messages" />
<fieldset>
<legend>Receiver</legend>
<%-- (1) --%>
<form:errors path="receiverAddress"
cssClass="error-messages" />
<%-- (2) --%>
<form:label path="receiverAddress.name"
cssErrorClass="error-label">Name:</form:label>
<form:input path="receiverAddress.name"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.name"
cssClass="error-messages" />
<form:label path="receiverAddress.postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="receiverAddress.postcode"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.postcode"
cssClass="error-messages" />
<form:label path="receiverAddress.address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="receiverAddress.address"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.address"
cssClass="error-messages" />
</fieldset>
<fieldset>
<legend>Sender</legend>
<form:errors path="senderAddress"
cssClass="error-messages" />
<form:label path="senderAddress.name"
cssErrorClass="error-label">Name:</form:label>
<form:input path="senderAddress.name"
cssErrorClass="error-input" />
<form:errors path="senderAddress.name"
cssClass="error-messages" />
<form:label path="senderAddress.postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="senderAddress.postcode"
cssErrorClass="error-input" />
<form:errors path="senderAddress.postcode"
cssClass="error-messages" />
<form:label path="senderAddress.address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="senderAddress.address"
cssErrorClass="error-input" />
<form:errors path="senderAddress.address"
cssClass="error-messages" />
</fieldset>
<form:button name="confirm">Confirm</form:button>
</form:form>
</body>
</html>
When receiverAddress.name
, receiverAddress.postcode
, receiverAddress.address
are not sent as
request parameters due to invalid operation, receiverAddress
is considered as null
and error message is displayed.
Validation of nested bean is enabled for collections also.
Add a field such that up to 3 addresses can be registered in “user registration” form explained at the beginning.
Add list of AddressForm
as a field in the form class.
package com.example.sample.app.validation;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min(0)
@Max(200)
private Integer age;
@NotNull
@Size(min = 1, max = 3) // (1)
@Valid
private List<AddressForm> addresses;
// omitted setter/getter
<form:form modelAttribute="userForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name" cssErrorClass="error-label">Name:</form:label>
<form:input path="name" cssErrorClass="error-input" />
<form:errors path="name" cssClass="error-messages" />
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<form:errors path="email" cssClass="error-messages" />
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<form:errors path="age" cssClass="error-messages" />
<form:errors path="addresses" cssClass="error-messages" /><%-- (1) --%>
<c:forEach items="${userForm.addresses}" varStatus="status"><%-- (2) --%>
<fieldset class="address">
<legend>Address${f:h(status.index + 1)}</legend>
<form:label path="addresses[${status.index}].name"
cssErrorClass="error-label">Name:</form:label><%-- (3) --%>
<form:input path="addresses[${status.index}].name"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].name"
cssClass="error-messages" />
<form:label path="addresses[${status.index}].postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="addresses[${status.index}].postcode"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].postcode"
cssClass="error-messages" />
<form:label path="addresses[${status.index}].address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="addresses[${status.index}].address"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].address"
cssClass="error-messages" />
<c:if test="${status.index > 0}">
<button class="remove-address-button">Remove</button>
</c:if>
</fieldset>
</c:forEach>
<button id="add-address-button">Add address</button>
<form:button name="confirm">Confirm</form:button>
</form:form>
<script type="text/javascript"
src="${pageContext.request.contextPath}/resources/vendor/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript"
src="${pageContext.request.contextPath}/resources/app/js/AddressesView.js"></script>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
List<AddressForm> addresses = new ArrayList<AddressForm>();
addresses.add(new AddressForm());
form.setAddresses(addresses); // (1)
return form;
@RequestMapping(value = "create", method = RequestMethod.GET, params = "form")
public String createForm() {
return "user/createForm";
@RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm")
public String createConfirm(@Validated UserForm form, BindingResult result) {
if (result.hasErrors()) {
return "user/createForm";
return "user/createConfirm";
JavaScript
Below is the JavaScript to dynamically add address input field. However, the explanation of this code is omitted as it is not required.
// webapp/resources/app/js/AddressesView.js
function AddressesView() {
this.addressSize = $('fieldset.address').size();
AddressesView.prototype.addAddress = function() {
var $address = $('fieldset.address');
var newHtml = addressTemplate(this.addressSize++);
$address.last().next().after($(newHtml));
AddressesView.prototype.removeAddress = function($fieldset) {
$fieldset.next().remove(); // remove <br>
$fieldset.remove(); // remove <fieldset>
function addressTemplate(number) {
return '\
<fieldset class="address">\
<legend>Address' + (number + 1) + '</legend>\
<label for="addresses' + number + '.name">Name:</label>\
<input id="addresses' + number + '.name" name="addresses[' + number + '].name" type="text" value=""/><br>\
<label for="addresses' + number + '.postcode">Postcode:</label>\
<input id="addresses' + number + '.postcode" name="addresses[' + number + '].postcode" type="text" value=""/><br>\
<label for="addresses' + number + '.address">Address:</label>\
<input id="addresses' + number + '.address" name="addresses[' + number + '].address" type="text" value=""/><br>\
<button class="remove-address-button">Remove</button>\
</fieldset>\
$(function() {
var addressesView = new AddressesView();
$('#add-address-button').on('click', function(e) {
e.preventDefault();
addressesView.addAddress();
$(document).on('click', '.remove-address-button', function(e) {
if (this === e.target) {
e.preventDefault();
var $this = $(this); // this button
var $fieldset = $this.parent(); // fieldset
addressesView.removeAddress($fieldset);
4.1.2.2.4. Grouped validation¶
By creating validation group, input validation rules for a field can be specified for each group.
In the “new user registration” example, add “Must be an adult” rule for the age
field.
Add country
field also as “Adult” rules differ with country.
To specify group in Bean Validation, set any java.lang.Class
object representing a group in group
attribute of the annotation.
Create the following 3 groups (interface) here.
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
// (1)
public static interface Chinese {
public static interface Japanese {
public static interface Singaporean {
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min.List({ // (2)
@Min(value = 18, groups = Chinese.class), // (3)
@Min(value = 20, groups = Japanese.class),
@Min(value = 21, groups = Singaporean.class)
@Max(200)
private Integer age;
@NotNull
@Size(min = 2, max = 2)
private String country; // (4)
// omitted setter/getter
Specify corresponding group class in the group
attribute, in order to define rules for each group.
When group
attribute is not specified, javax.validation.groups.Default
group is used.
There are no major changes in JSP.
<form:form modelAttribute="userForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name" cssErrorClass="error-label">Name:</form:label>
<form:input path="name" cssErrorClass="error-input" />
<form:errors path="name" cssClass="error-messages" />
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<form:errors path="email" cssClass="error-messages" />
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<form:errors path="age" cssClass="error-messages" />
<form:label path="country" cssErrorClass="error-label">Country:</form:label>
<form:select path="country" cssErrorClass="error-input">
<form:option value="cn">China</form:option>
<form:option value="jp">Japan</form:option>
<form:option value="sg">Singapore</form:option>
</form:select>
<form:errors path="country" cssClass="error-messages" />
<form:button name="confirm">Confirm</form:button>
</form:form>
Controller class
By giving a group name to @Validated
annotation, the rules defined for that group will be applied.
package com.example.sample.app.validation;
import javax.validation.groups.Default;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.sample.app.validation.UserForm.Chinese;
import com.example.sample.app.validation.UserForm.Japanese;
import com.example.sample.app.validation.UserForm.Singaporean;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
return form;
@RequestMapping(value = "create", method = RequestMethod.GET, params = "form")
public String createForm() {
return "user/createForm";
String createConfirm(UserForm form, BindingResult result) {
if (result.hasErrors()) {
return "user/createForm";
return "user/createConfirm";
@RequestMapping(value = "create", method = RequestMethod.POST, params = {
"confirm", /* (1) */ "country=cn" })
public String createConfirmForChinese(@Validated({ /* (2) */ Chinese.class,
Default.class }) UserForm form, BindingResult result) {
return createConfirm(form, result);
@RequestMapping(value = "create", method = RequestMethod.POST, params = {
"confirm", "country=jp" })
public String createConfirmForJapanese(@Validated({ Japanese.class,
Default.class }) UserForm form, BindingResult result) {
return createConfirm(form, result);
@RequestMapping(value = "create", method = RequestMethod.POST, params = {
"confirm", "country=sg" })
public String createConfirmForSingaporean(@Validated({ Singaporean.class,
Default.class }) UserForm form, BindingResult result) {
return createConfirm(form, result);
Warning
Implementation of this Controller is inadequate; there is no handling when country
value is neither “cn”, “jp” nor “sg”.
400 error is returned when unexpected country
value is encountered.
Next, we can think of a condition where the number of countries increase and adult condition of 18 years or more is be set as a default rule.
Rules are as follows.
Form class
In order to specify a value to Default
group (18 years or more), all groups should be specified explicitly in other annotations as well.
package com.example.sample.app.validation;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.groups.Default;
import org.hibernate.validator.constraints.Email;
public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
public static interface Japanese {
public static interface Singaporean {
@NotNull(groups = { Default.class, Japanese.class, Singaporean.class }) // (1)
@Size(min = 1, max = 20, groups = { Default.class, Japanese.class,
Singaporean.class })
private String name;
@NotNull(groups = { Default.class, Japanese.class, Singaporean.class })
@Size(min = 1, max = 50, groups = { Default.class, Japanese.class,
Singaporean.class })
@Email(groups = { Default.class, Japanese.class, Singaporean.class })
private String email;
@NotNull(groups = { Default.class, Japanese.class, Singaporean.class })
@Min.List({
@Min(value = 18, groups = Default.class), // (2)
@Min(value = 20, groups = Japanese.class),
@Min(value = 21, groups = Singaporean.class) })
@Max(200)
private Integer age;
@NotNull(groups = { Default.class, Japanese.class, Singaporean.class })
@Size(min = 2, max = 2, groups = { Default.class, Japanese.class,
Singaporean.class })
private String country;
// omitted setter/getter
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.sample.app.validation.UserForm.Japanese;
import com.example.sample.app.validation.UserForm.Singaporean;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
return form;
@RequestMapping(value = "create", method = RequestMethod.GET, params = "form")
public String createForm() {
return "user/createForm";
String createConfirm(UserForm form, BindingResult result) {
if (result.hasErrors()) {
return "user/createForm";
return "user/createConfirm";
@RequestMapping(value = "create", method = RequestMethod.POST, params = { "confirm" })
public String createConfirmForDefault(@Validated /* (1) */ UserForm form,
BindingResult result) {
return createConfirm(form, result);
@RequestMapping(value = "create", method = RequestMethod.POST, params = {
"confirm", "country=jp" })
public String createConfirmForJapanese(
@Validated(Japanese.class) /* (2) */ UserForm form, BindingResult result) {
return createConfirm(form, result);
@RequestMapping(value = "create", method = RequestMethod.POST, params = {
"confirm", "country=sg" })
public String createConfirmForSingaporean(
@Validated(Singaporean.class) UserForm form, BindingResult result) {
return createConfirm(form, result);
Using Default
group in Controller class
group
attribute need not be set for the rules that need not be grouped.
Since all patterns of group should be defined, it is difficult to define when there are many group patterns.
Should be used when there are only a limited number of group patterns (New create group, Update group and Delete group)
Using Default
group in form class
Since only the groups that do not belong to the default group need to be defined, it can be handled even if there are many patterns.
group
attribute should be set for the rules that need not be grouped making the process complicated.
Should be used when there are many group patterns and majority of patterns have a common value.
If none of the above decision points are applicable, then using Bean Validation itself might not be a good idea.After reviewing the design, usage of Spring Validator or implementation of validation in business logic should be considered.
In the examples explained so far, the switching of group validation is carried out using request parameter and parameter that can be specified in @RequestMapping
annotation.
It is not possible to switch between groups, if switching is to be performed based on permissions in authentication object or any information which cannot be handled by @RequestMapping
annotation.
In such a case, @Validated
annotation must not be used but org.springframework.validation.SmartValidator
must be used. Group validation can be performed inside the handler method of controller.
@Controller
@RequestMapping("user")
public class UserController {
@Inject
SmartValidator smartValidator; // (1)
// omitted
@RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm")
public String createConfirm(/* (2) */ UserForm form, BindingResult result) {
// (3)
Class<?> validationGroup = Default.class;
// logic to determine validation group
// if (xxx) {
// validationGroup = Xxx.class;
smartValidator.validate(form, result, validationGroup); // (4)
if (result.hasErrors()) {
return "user/createForm";
return "user/createConfirm";
4.1.2.3. Correlation item check¶
For the validation of correlated items,
Spring Validator(Validator
implementing org.springframework.validation.Validator
interface)
or Bean Validation must be used.
Each one of above has been explained below. However, before that, their features and usage have been explained.
4.1.2.3.1. Correlation item check implementation using Spring Validator¶
Implementation method is explained with the help of “reset password” process as an example.
Implement the following rules. Following rules are provided in the “reset password” form.
Check rule “Must be same as confirmPassword” is validation of correlated items as password
field and passwordConfirm
field should have the same value.
Form class
other than validation of correlated items, implement using Bean Validation annotation.
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class PasswordResetForm implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(min = 8)
private String password;
private String confirmPassword;
// omitted setter/getter
Password is normally saved in database after hashing it, hence there is no need to check the maximum number of characters.
Validator class
Implement validation of correlated items using org.springframework.validation.Validator
interface.
package com.example.sample.app.validation;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Component // (1)
public class PasswordEqualsValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz); // (2)
@Override
public void validate(Object target, Errors errors) {
if (errors.hasFieldErrors("password")) { // (3)
return;
PasswordResetForm form = (PasswordResetForm) target;
String password = form.getPassword();
String confirmPassword = form.getConfirmPassword();
if (!password.equals(confirmPassword)) { // (4)
errors.rejectValue(/* (5) */ "password",
/* (6) */ "PasswordEqualsValidator.passwordResetForm.password",
/* (7) */ "password and confirm password must be same.");
If an error occurs at the target fields during a single item check, do not perform correlation check in this Validator.
If it is necessary to perform the correlation check, this determination logic is not required.
Specify code name of error message. Here, code is
“[validator name].[form attribute name].[property name]”
Refer to Messages to be defined in application-messages.properties for mesaage definition.
Spring Validator implementation class should be placed in the same package as the Controller.
Controller class
package com.example.sample.app.validation;
import javax.inject.Inject;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
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;
@Controller
@RequestMapping("password")
public class PasswordResetController {
@Inject
PasswordEqualsValidator passwordEqualsValidator; // (1)
@ModelAttribute
public PasswordResetForm setupForm() {
return new PasswordResetForm();
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(passwordEqualsValidator); // (2)
@RequestMapping(value = "reset", method = RequestMethod.GET, params = "form")
public String resetForm() {
return "password/resetForm";
@RequestMapping(value = "reset", method = RequestMethod.POST)
public String reset(@Validated PasswordResetForm form, BindingResult result) { // (3)
if (result.hasErrors()) {
return "password/resetForm";
return "redirect:/password/reset?complete";
@RequestMapping(value = "reset", method = RequestMethod.GET, params = "complete")
public String resetComplete() {
return "password/resetComplete";
In the method having @InitBinder
annotation, add Validators using WebDataBinder.addValidators
method.
By this, the added Validator is called when validation is executed with the use of @Validated
annotation.
<form:form modelAttribute="passwordResetForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/password/reset">
<form:label path="password" cssErrorClass="error-label">Password:</form:label>
<form:password path="password" cssErrorClass="error-input" />
<form:errors path="password" cssClass="error-messages" />
<form:label path="confirmPassword" cssErrorClass="error-label">Password (Confirm):</form:label>
<form:password path="confirmPassword"
cssErrorClass="error-input" />
<form:errors path="confirmPassword" cssClass="error-messages" />
<form:button>Reset</form:button>
</form:form>
</body>
</html>
Error information can be set for multiple fields for correlation check.
However, displaying error messages and applying style must always be performed in a set and only one part of the tasks cannot not be performed.
When you want to apply style to both the fields wherein correlation check error has occurred however you want to display only one error message,
it can be done by setting a null string in the error message.
An example is given below wherein style is applied to password
field and confirmPassword
field and error message is displayed only in password
field.
package com.example.sample.app.validation;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Component
public class PasswordEqualsValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz);
@Override
public void validate(Object target, Errors errors) {
// omitted
if (!password.equals(confirmPassword)) {
// register a field error for password
errors.rejectValue("password",
"PasswordEqualsValidator.passwordResetForm.password",
"password and confirm password must be same.");
// register a field error for confirmPassword
errors.rejectValue("confirmPassword", // (1)
"PasswordEqualsValidator.passwordResetForm.confirmPassword", // (2)
""); // (3)
Specify code name of error message. Specify a null string in the corresponding error message at that time.
For message definition, refer Messages to be defined in application-messages.properties.
When multiple forms are used in a single controller, model name should be specified in @InitBinder("xxx")
in order to limit the target of Validator.
@Controller
@RequestMapping("xxx")
public class XxxController {
// omitted
@ModelAttribute("aaa")
public AaaForm() {
return new AaaForm();
@ModelAttribute("bbb")
public BbbForm() {
return new BbbForm();
@InitBinder("aaa")
public void initBinderForAaa(WebDataBinder binder) {
// add validators for AaaForm
binder.addValidators(aaaValidator);
@InitBinder("bbb")
public void initBinderForBbb(WebDataBinder binder) {
// add validators for BbbForm
binder.addValidators(bbbValidator);
// omitted
To change the check contents of correlated items check rules in accordance with a validation group (for example: To implement correlated items check only when specific validation group is specified, etc.), it is better to switch the process within validate method by implementing org.springframework.validation.SmartValidator
interface instead of implementing org.springframework.validation.Validator
interface.
package com.example.sample.app.validation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
@Component
public class PasswordEqualsValidator implements SmartValidator { // Implements SmartValidator instead of Validator interface
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz);
@Override
public void validate(Object target, Errors errors) {
validate(target, errors, new Object[] {});
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
// Check validationHints(groups) and apply validation logic only when 'Update.class' is specified
if (ArrayUtils.contains(validationHints, Update.class)) {
PasswordResetForm form = (PasswordResetForm) target;
String password = form.getPassword();
String confirmPassword = form.getConfirmPassword();
// omitted...
4.1.2.3.2. Implementation of input check of correlated items using Bean Validation¶
Independent validation rules should be added to implement validation of correlated items using Bean Validation.
It is explained in How to extend.
4.1.2.4. Definition of error messages¶
Method to change error messages of input validation is explained.
Error messages of Bean Validation in Spring MVC are resolved in the following order.
If there is any message which matches with the rule, among the messages defined in org.springframework.context.MessageSource
, then it is to be used as error message (Spring rule).
For default rules of Spring, refer to “JavaDoc of DefaultMessageCodesResolver of DefaultMessageCodesResolver”.
If message cannot be found as mentioned in step 1, then error message is acquired from the message
attribute of the annotation. (Bean Validation rule)
- When the value of
message
attribute is not in “{message key}” format, use that text as error message.
- When the value of
message
attribute is in “{message key}” format, search messages corresponding to message key from ValidationMessages.properties under classpath.
- When message corresponding to message key is defined, use that message
- When message corresponding to message key is not defined, use “{message key}” as error message
Basically, it is recommended to define error messages in properties file.
Messages should be defined at following places.
- properties file read by
org.springframework.context.MessageSource
- ValidationMessages.properties under classpath
Considering that the following settings are done in applicationContext.xml, former is called as “application-messages.properties” and latter is called “ValidationMessages.properties”.
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<value>i18n/application-messages</value>
</list>
</property>
</bean>
Warning
Multiple ValidationMessages.properties
files should not exist directly under class path.
If multiple ValidationMessages.properties
files exist directly under class path,
an appropriate message may not be displayed, as either one file of them is read leaving rest of the files unread.
- When adopting multi project structure, please take care so that
ValidationMessages.properties
file is not placed in multiple projects.
- When distributing common parts for Bean Validation as jar file, please take care so that
ValidationMessages.properties
file is not included in jar file.
Further, when a project is created from Blank project of version 1.0.2.RELEASE or higher,
ValidationMessages.properties
is stored directly under xxx-web/src/main/resources
.
When ValidationMessages.properties is not provided, Default messages provided by Hibernate Validator is used.
4.1.2.4.1. Messages to be defined in ValidationMessages.properties¶
Define messages for message key specified in message
attribute of Bean Validation annotation of
ValidationMessages.properties under class path (normal src/main/resources).
It is explained below using the following form used at the beginning of Basic single item check.
Form class (re-displayed)
public class UserForm implements Serializable {
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min(0)
@Max(200)
private Integer age;
// omitted getter/setter
ValidationMessages.properties
Change error messages of @NotNull
, @Size
, @Min
, @Max
, @Email
.
javax.validation.constraints.NotNull.message=is required.
# (1)
javax.validation.constraints.Size.message=size is not in the range {min} through {max}.
# (2)
javax.validation.constraints.Min.message=cannot be less than {value}.
javax.validation.constraints.Max.message=cannot be greater than {value}.
org.hibernate.validator.constraints.Email.message=is an invalid e-mail address.
Warning
Since {FQCN of annotation.message}
is set in message
attribute in Bean Validation standard annotations and independent Hibernate Validator annotations,
messages can be defined in properties file in the above format. However, since all annotations may not be in this format, Javadoc or source code of the target annotation should be checked.
FQCN of annotation.message = Message
Add {0}
to message as shown below when field name is to be included in error message.
- ValidationMessages.properties
Change error message of @NotNull
, @Size
, @Min
, @Max
and @Email
.
javax.validation.constraints.NotNull.message="{0}" is required.
javax.validation.constraints.Size.message=The size of "{0}" is not in the range {min} through {max}.
javax.validation.constraints.Min.message="{0}" cannot be less than {value}.
javax.validation.constraints.Max.message="{0}" cannot be greater than {value}.
org.hibernate.validator.constraints.Email.message="{0}" is an invalid e-mail address.
In this way, property name of form class gets displayed on the screen and so it is not user friendly.
To display an appropriate field name, it should be defined in application-messages.properties in the following format.
form property name=field name to be displayed
Adding the same to our example.
application-messages.properties
name=Name
email=Email
age=Age
Inserting field name in place of {0}
is the functionality of Spring and not of Bean Validation.
Therefore, the settings for changing field name should be defined in application-messages.properties(ResourceBundleMessageSource
) which is directly under Spring management.
In Bean Validation 1.1,
it is possible to use Expression Language (hereafter referred to as “EL expression”) in a message specified in ValidationMessages.properties
.
Hibernate Validator 5.x supports Expression Language 2.2 or higher version.
Executable EL expression version differs depending on the version of application server.
Therefore when EL expression is to be used, it should be used after confirming the version of EL expression supported by application server.
Following is an example of using EL expression in a message which is defined in ValidationMessages.properties
provided by Hibernate Validator by default.
# ...
# (1)
javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value}
# ...
An EL expression is a part of “${inclusive == true ? 'or equal to ' : ''}
” in a message.
From the above mentioned definition of message, 2 patterns of messages are created as given below.
- must be less than or equal to {value}
- must be less than {value}
(A value specified in value
attribute of @DecimalMax
annotation is embedded in {value}
part)
Former is created when true
is specified (or when not specified) in inclusive
attribute of @DecimalMax
annotation,
Latter is created when false
is specified in inclusive
attribute of @DecimalMax
annotation.
For handling of EL expressions in Bean Validation refer to:
Hibernate Validator Reference Guide(Interpolation with message expressions).
4.1.2.4.2. Messages to be defined in application-messages.properties¶
Default messages to be used in system are defined in ValidationMessages.properties
however, depending on the screen, they may have to be changed from the default value.
In this case, define messages in the following format in application-messages.properties.
[annotation name].[form attribute name].[property name] = [target message]
Apply “Messages to be defined in ValidationMessages.properties” and override the message for email
and age
field using the below settings.
application-messages.properties
# override messages
# for email field
Size.userForm.email=The size of "{0}" must be between {2} and {1}.
# for age field
NotNull.userForm.age="{0}" is compulsory.
Min.userForm.age="{0}" must be greater than or equal to {1}.
Max.userForm.age="{0}" must be less than or equal to {1}.
# filed names
name=Name
email=Email
age=Age
Value of attributes of the annotation gets inserted after {1}
onwards.
Incidentally, index position of attribute values are alphabetical ordering(ascending order) of attribute names.
For example, index positions of @Size
are as follow:
{0}
: property name (physical name or logical name)
{1}
: value of max
attribute
{2}
: value of min
attribute
For specification details, refer to JavaDoc of SpringValidatorAdapter.
Error messages are changed as follows.
There are other formats as well for the message key format of application-messages.properties;
however, if it is used with the purpose of overwriting some default messages, it should be in [annotation name].[form attribute name].[property name]
format.
4.1.3. How to extend¶
Other than standard check rules, bean validation has a mechanism to develop annotations for independent rules .
The method of creating independent rules can be widely classified into the following two broader criteria.
- Combination of existing rules
- Creation of new rules
Basically, the below template can be used to create annotation for each rule.
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Xxx {
String message() default "{com.example.common.validation.Xxx.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface List {
Xxx[] value();
4.1.3.1. Creation of Bean Validation annotation by combining existing rules¶
Consider the following restrictions at the system level and domain level respectively.
At the system level,
- String must be single byte alphanumeric characters
- Numbers must be positive
Or at the domain level,
- “User ID” must be between 4 and 20 single byte characters
- “Age” must be between 1 year and 150 years
These can be implemented by combining @Pattern
, @Size
, @Min
, @Max
of the existing rules.
However, if the same rules are to be used at multiple places, settings get distributed and maintainability worsens.
One rule can be created by combining multiple rules.
There is an advantage to be able to have not only common regular expression pattern and maximum/minimum values but also error message when an independent annotation is created.
By this, reusability and maintainability increases. Even if multiple rules are not combined, it also proves beneficial if used only to give specific value to an attribute.
Implementation example is shown below.
Implementation example of @Alphanumeric
annotation which is restricted to single byte alphanumeric characters
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation // (1)
@Pattern(regexp = "[a-zA-Z0-9]*") // (2)
public @interface AlphaNumeric {
String message() default "{com.example.common.validation.AlphaNumeric.message}"; // (3)
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface List {
AlphaNumeric[] value();
Implementation example of @NotNegative
annotation which is restricted to positive number
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Min;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@Min(value = 0)
public @interface NotNegative {
String message() default "{com.example.common.validation.NotNegative.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface List {
NotNegative[] value();
Implementation example of @UserId
annotation which regulates the format of “User ID”.
package com.example.sample.domain.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@Size(min = 4, max = 20)
@Pattern(regexp = "[a-z]*")
public @interface UserId {
String message() default "{com.example.sample.domain.validation.UserId.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface List {
UserId[] value();