Backend Development 11 min read

Master Spring Boot Data Binding: Constructor & Property Binding with Custom Editors

This article explains how Spring Boot 3.4.2 handles data binding using constructors and property setters, demonstrates practical examples with code, shows how to resolve binding errors with BindingResult, and guides you through creating and registering custom PropertyEditors for complex type conversion.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot Data Binding: Constructor & Property Binding with Custom Editors

1. Introduction

Data binding maps user input (a map of property paths to values) to target objects following JavaBeans conventions. The main class DataBinder supports two binding modes:

Constructor binding : binds input to a public constructor, matching constructor parameters.

Property binding : binds input to setter methods, matching keys to object properties.

Both modes can be used together or separately.

2. Practical Examples

2.1 Constructor Binding

Three steps are required:

Create a

DataBinder

with

null

as the target object.

Set

targetType

to the desired class.

Call

construct

with a

ValueResolver

that supplies the input values.

<code>public class User {
  private Long id;
  private String name;
  private Integer age;
  // getters and setters
}

Map<String, Object> values = Map.of(
  "id", "666",
  "age", "33",
  "f_name", "Pack"
);
DataBinder binder = new DataBinder(null);
binder.setTargetType(ResolvableType.forClass(User.class));
ValueResolver valueResolver = new ValueResolver() {
  @Override
  public Object resolveValue(String name, Class<?> type) {
    return values.get(name);
  }
  @Override
  public Set<String> getNames() {
    return Set.of("id", "name", "age", "email", "address");
  }
};
binder.construct(valueResolver);
Object target = binder.getTarget();
System.err.println(target);
</code>

The initial run shows no data bound because

User

only has a default constructor. Adding a matching constructor fixes the binding:

<code>public User(Long id, String name, Integer age) {
  this.id = id;
  this.name = name;
  this.age = age;
}
</code>

If multiple constructors exist, Spring prefers the no‑arg constructor; if none exists, it selects a suitable argument constructor.

2.2 Property Binding

The BeanWrapper API allows setting and retrieving properties, including nested and indexed properties. Example:

<code>public class Department {
  private String code;
  private String deptName;
  private Employee employee;
}
public class Employee {
  private String name;
  private Integer age;
}
</code>
<code>Department target = new Department();
BeanWrapper depart = new BeanWrapperImpl(target);
depart.setPropertyValue("code", "D-0001");
PropertyValue pv = new PropertyValue("deptName", "研发部");
depart.setPropertyValue(pv);
BeanWrapper emp = new BeanWrapperImpl(new Employee());
emp.setPropertyValue("name", "Pack");
emp.setPropertyValue("age", "23");
depart.setPropertyValue("employee", emp.getWrappedInstance());
System.err.println(depart.getWrappedInstance());
</code>

Binding request parameters to an object can be done with

ServletRequestParameterPropertyValues

:

<code>@GetMapping("/save")
public ResponseEntity<?> save(HttpServletRequest request) {
  BeanWrapper bw = new BeanWrapperImpl(Address.class);
  PropertyValues pvs = new ServletRequestParameterPropertyValues(request);
  bw.setPropertyValues(pvs);
  return ResponseEntity.ok(bw.getWrappedInstance());
}
</code>

2.3 Type Conversion

Spring uses PropertyEditor implementations to convert between strings and target types (e.g., dates). Custom editors can be registered to handle complex types such as

Address

:

<code>public class AddressEditor extends PropertyEditorSupport {
  @Override
  public void setAsText(String text) throws IllegalArgumentException {
    if (text == null || "".equals(text)) {
      setValue(null);
    } else {
      String[] parts = text.split(",");
      setValue(new Address(parts[0], parts[1]));
    }
  }
}
</code>

Spring automatically discovers an editor named

AddressEditor

in the same package as

Address

. To register it programmatically, use CustomEditorConfigurer :

<code>@Bean
CustomEditorConfigurer customEditorConfigurer() {
  CustomEditorConfigurer configurer = new CustomEditorConfigurer();
  configurer.setCustomEditors(Map.of(Address.class, AddressEditor.class));
  return configurer;
}
</code>

Alternatively, implement PropertyEditorRegistrar for reusable registration:

<code>@Component
public final class AddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
  @Override
  public void registerCustomEditors(PropertyEditorRegistry registry) {
    registry.registerCustomEditor(Address.class, new AddressEditor());
  }
}
</code>

Use the registrar in a controller via

@InitBinder

:

<code>@RestController
@RequestMapping("/persons")
public class PersonController {
  private final AddressPropertyEditorRegistrar registrar;
  public PersonController(AddressPropertyEditorRegistrar registrar) {
    this.registrar = registrar;
  }
  @InitBinder
  public void init(WebDataBinder binder) {
    registrar.registerCustomEditors(binder);
  }
  @GetMapping("/create")
  public ResponseEntity<?> create(Person person) {
    return ResponseEntity.ok(person);
  }
}
</code>

These techniques enable robust data binding, error handling with

BindingResult

, and custom type conversion in Spring Boot applications.

Spring Bootdata-bindingProperty BindingConstructor BindingCustom PropertyEditor
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.