javaspringvalidationspring-mvcspring-validator

How exactly does the supports() method of the Spring Validator interface work?


I am pretty new in Spring MVC and I have the following doubt about how this validator example works:

So I have this search.jsp page in which the user can search a product by 2 fields criteria (the product name and the category to which the product belongs):

<%@ 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 uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!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>Search</title>
<jsp:include page="/WEB-INF/views/include/head-include.jsp" />
</head>
<body>
    <div class="container">
    <h2>Search Products</h2>
    <div class="form-group form">
    <form:form name="input"  method="get"
        modelAttribute="product" action="products">
        <div>
            <label>Name: </label>
            <form:input type="text" path="name" class="form-control"  />
            <form:errors path="name" />
        </div>
        <div>
            <label>Category:</label>
            <form:select path="category.id" class="form-control" >
                <form:option path="category.id" value="0" label="--- All Categories ---"/>
                <form:options path="category.id" items="${categories}" itemLabel="name" itemValue="id" />
            </form:select>
        </div>
        <div>
            <input type="submit" value="Submit" class="form-control">
        </div>


    </form:form>
    </div>
    
    <h2>Search Results:</h2>
    
    <c:forEach var="product" items="${results}">
        <c:out value="${product.name}"/> <br/>
    </c:forEach>
    </div>

</body>
</html>

So, in the tutorial are introduced some restriction on the input of the 2 fields of the previous view implementing a custom validator. In the specific the user have to:

  1. The user have to insert a product name or a category

  2. The name length have to be at least composed by 3 characters.

  3. The user can chose only by name or category but not on some other field of the Product model object

To implement this custom validator this ProductSearchValidator class is created that implements the Spring Validator interface:

package com.packtpub.springmvc.chocolatestore;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.packtpub.springmvc.chocolatestore.model.Product;

@Component
public class ProductSearchValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return Product.class.isAssignableFrom(clazz);  
    }

    @Override
    public void validate(Object target, Errors errors) {
        Product product = (Product) target;
        String name = product.getName();
        if (!StringUtils.hasLength(name) && (product.getCategory() == null || product.getCategory().getId() == 0)) {
            errors.rejectValue("name", "required", "Either name or category is required");
        } else if ((product.getCategory() == null || product.getCategory().getId() == 0) && name.trim().length() < 3) {
            errors.rejectValue("name", "tooShort", "Please enter at least 3 letters");
        }

    }

}

From what I have understand the supports() method say if a specific instance of a class can be validate from this validator and the validate() method implement the specific validation logic.

OK, the validate() method is pretty clear to me (it implement the previous 3 specified restriction).

The thing that I can't understand is how exactly does the supports() method work:

@Override
public boolean supports(Class<?> clazz) {
    return Product.class.isAssignableFrom(clazz);  
}

What represent its input parameter (Class<?> clazz)? And what represent the returned boolean? (what is the meaning of the Product.class.isAssignableFrom(clazz) evaluation?)


Solution

  • the supports() method say if a specific instance of a class can be validate from this validator

    No. It says if a specific class is supported by the validator. When validating an object, the object itself is not passed to supports(). What is passed to supports() is the class of the object.

    So, before validating an instance of Product, Spring calls supports() with Product.class as argument.

    Before validating an instance of SubClassOfProduct, Spring calls supports() with SubClassOfProduct.class as argument.

    In both cases, since the Product.class is assignable from Product.class / SubclassOfProduct.class, the method returns true. If you tried to validate a User with that validator, it would return false. Indeed, you can do

    Product p = someProductInstance;
    

    You can also do

    Product p = someSubClassOfProductInstance;
    

    But you can't do

    Product p = someUserInstance;
    

    That's what Product.class.isAssignableFrom(clazz) means.

    See the relevant javadocs, which basically explain what I just explained: