Close

Spring MVC - Data Binding with Immutable Bean Classes in Spring 5

[Last Updated: Nov 12, 2018]

Starting Spring 5, data binding can work with immutable classes now. Previously there must be a no argument constructor and suitable setters to initialize the bean against the request parameters.

From SPR-15199:

We're now automatically detecting data classes with a single public constructor, resolving the constructor arguments against the request parameters, as long as the parameter names are retained or an @ConstructorProperties annotation is declared. This works for Spring MVC as well as WebFlux.

@ConstructorProperties is a Java annotation meant to use on a constructor to indicate how the parameters of that constructor correspond to the constructed bean's getter methods.

Example

Example Immutable Beans

package com.logicbig.example;

import java.beans.ConstructorProperties;

public class CustomerInfo {
  private String customerId;
  private String zipCode;

  @ConstructorProperties({"id", "zip"})
  public CustomerInfo(String customerId, String zipCode) {
      this.customerId = customerId;
      this.zipCode = zipCode;
  }

  public String getCustomerId() {
      return customerId;
  }

  public String getZipCode() {
      return zipCode;
  }

  @Override
  public String toString() {
      return "CustomerInfo{" +
              "customerId='" + customerId + '\'' +
              ", zipCode='" + zipCode + '\'' +
              '}';
  }
}
public class OrderInfo {

  private final String id;
  private final String zip;

  public OrderInfo(String id, String zip) {
      this.id = id;
      this.zip = zip;
  }

  public String getId() {
      return id;
  }

  public String getZip() {
      return zip;
  }

  @Override
  public String toString() {
      return "OrderInfo{" +
              "id='" + id + '\'' +
              ", zip='" + zip + '\'' +
              '}';
  }
}

Example Controller

@RestController
public class CustomerController {

  @GetMapping("/customer")
  public String getCustomerInfo(CustomerInfo ci) {
      return ci.toString();
  }

  @GetMapping("/order")
  public String getCustomerInfo(OrderInfo oi) {
      return oi.toString();
  }
}

JavaConfig

@EnableWebMvc
@Configuration
@ComponentScan
public class AppConfig{

}

Running

To try examples, run embedded tomcat (configured in pom.xml of example project below):

mvn tomcat7:run-war

Output:

In versions before Spring 5.0, following exception would be thrown:

SEVERE: Servlet.service() for servlet [springDispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.logicbig.example.CustomerInfo]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.logicbig.example.CustomerInfo.<init>()] with root cause
java.lang.NoSuchMethodException: com.logicbig.example.CustomerInfo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
......

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 5.1.2.RELEASE: Spring Web MVC.
  • javax.servlet-api 3.0.1 Java Servlet API
  • JDK 1.8
  • Maven 3.5.4

Sporing 5 - Data Binding with Immutable Bean Classes Select All Download
  • spring-5-immutable-class-data-binding
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • CustomerInfo.java

    See Also