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