Spring - Mixed validation using JSR 349 Annotations and Spring validator

[Last Updated: Jan 21, 2022]

Using JSR 349/380 annotations is the recommended way to perform bean validations. Although it's not always possible to use it for complex validation.

Let's say in our previous examples, Order object has an additional field, customerId, which has to be validated against the ids loaded from a database. In those kind of scenarios pure JSR 349 annotation based approach is not very suitable.

Spring provides enough flexibility to mix JSR 349/380 annotation with Spring's org.springframework.validation.Validator approaches.


The object to be validated

package com.logicbig.example;

import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Date;

public class Order {
  @NotNull(message = "{date.empty}")
  @Future(message = "{date.future}")
  private Date date;

  @NotNull(message = "{price.empty}")
  @DecimalMin(value = "0", inclusive = false, message = "{price.invalid}")
  private BigDecimal price;

  String customerId;

Implementing Spring Validator interface

package com.logicbig.example;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class OrderValidator implements Validator {

  public boolean supports(Class<?> clazz) {
      return Order.class == clazz;

  public void validate(Object target, Errors errors) {
      ValidationUtils.rejectIfEmpty(errors, "customerId", "customerId.empty");

      Order order = (Order) target;
      if (order.getCustomerId() != null) {
          Customer customer = getCustomerById(order.getCustomerId());
          if (customer == null) {
                      new Object[]{order.getCustomerId()},
                      "Customer id is not valid");

  private Customer getCustomerById(String customerId) {
      //just for test returning null.
      // in real example the customer could be loop up in database etc.
      return null;

A generic approach to loop up validator and apply validation

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Validator;
import java.util.Locale;
import java.util.Map;

public class GenericValidator {

  ApplicationContext context;

  public boolean validateObject(Object objectToValidate) {

      Map<String, Validator> validatorMap = context.getBeansOfType(Validator.class);
      if (validatorMap == null) {
          return true;

      DataBinder binder = new DataBinder(objectToValidate);

      //in this example two validators are register OrderValidator
      // and LocalValidatorFactoryBean which will do JSR 349 validations.
      for (Validator validator : validatorMap.values()) {

          if (validator.supports(objectToValidate.getClass())) {

      BindingResult bindingResult = binder.getBindingResult();
      if (bindingResult.hasErrors()) {
          ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();

                  new Object[]{objectToValidate.getClass().getSimpleName()}, Locale.US));

                       .map(e -> messageSource.getMessage(e, Locale.US))
          return false;

      return true;

Validating Order object

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;

public class ClientBean {

  private GenericValidator genericValidator;

  public void processOrder(Order order) {
      if (genericValidator.validateObject(order)) {
          System.out.println("processing " + order);

Spring Configuration

package com.logicbig.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

public class Config {

  public ClientBean clientBean() {
      return new ClientBean();

  public Validator validatorFactory() {
      return new LocalValidatorFactoryBean();

  public OrderValidator orderValidator() {
      return new OrderValidator();

  public GenericValidator genericValidator() {
      return new GenericValidator();

Main class

package com.logicbig.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ValidationMixedExample {

  public static void main(String[] args) {

      AnnotationConfigApplicationContext context = new

      ClientBean clientBean = context.getBean(ClientBean.class);
      Order order = new Order();
      //  order.setPrice(BigDecimal.TEN);
      //  order.setDate(new Date(System.currentTimeMillis() + 100000));


Order has validation errors:
Price cannot be empty
Date cannot be empty
No customer found with id 111

Example Project

Dependencies and Technologies Used:

  • spring-context 4.3.3.RELEASE (Spring Context)
  • hibernate-validator 5.2.4.Final (Hibernate's Bean Validation (JSR-303) reference implementation)
  • javax.el-api 2.2.4 (Expression Language API 2.2)
  • javax.el 2.2.4 (Expression Language 2.2 Implementation)
  • JDK 1.8
  • Maven 3.8.1

Mixed validation using JSR 349 Annotations and Spring validator Select All Download
  • spring-mixing-jsr349-annotations-and-spring-validator
    • src
      • main
        • java
          • com
            • logicbig
              • example
          • resources

    See Also