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 ProjectDependencies 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
|