skip to content

What does registerCustomEditor do on a DataBinder/BeanWrapper, and when would you use it?

level: middleimportance: should knowfreq 38%

answer

  1. JavaBeans PropertyEditor: setAsText/getAsText
  2. extend PropertyEditorSupport
  3. @InitBinder in controllers
  4. path-scoped overload for per-field format
  5. stateful → not thread-safe → per-binder

basics

~20 s

registerCustomEditor registers a JavaBeans PropertyEditor that tells the binder how to turn a String into (and out of) a specific type — for example parsing a date format. It scopes conversion to that binder or property.

solid answer

~40 s

registerCustomEditor(Class<?> requiredType, PropertyEditor editor) plugs a JavaBeans java.beans.PropertyEditor into the binder so it knows how to convert String input to a given target type and back. A PropertyEditor implements setAsText(String) to parse and getAsText() to render; Spring ships helpers like CustomDateEditor and CustomNumberEditor, and you often extend PropertyEditorSupport. You can register it for a whole type, or scope it to a single property path via the (Class, String field, PropertyEditor) overload. In Spring MVC you typically do this inside an @InitBinder method so each request's WebDataBinder learns the conversion. It's the legacy per-binder conversion SPI; the modern, container-wide alternative is a ConversionService with Formatters — but registerCustomEditor remains the field-scoped, stateful-editor mechanism. Note PropertyEditors are not thread-safe, so a new instance is created per binder.

code

java · 20 lines
java
@Controller
public class EventController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        fmt.setLenient(false);
        // Applies to every java.util.Date property on the command object:
        binder.registerCustomEditor(Date.class, new CustomDateEditor(fmt, true));
        // Trim strings, convert empty to null:
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    @PostMapping("/events")
    public String create(@ModelAttribute Event event, BindingResult result) {
        if (result.hasErrors()) return "form";
        // event.startDate parsed via the CustomDateEditor
        return "redirect:/events";
    }
}

go deeper

for a junior

Know it teaches the binder how to parse a String into a type, e.g. a date format.

for a middle

Explain setAsText/getAsText, @InitBinder usage, and the type- vs path-scoped overloads.

for a senior

Discuss thread-safety (stateful editors, per-binder registration) and when to prefer a Formatter/ConversionService.

for a principal

Reason about legacy PropertyEditor SPI vs core.convert, migration tradeoffs, and keeping field-scoped stateful conversion where it genuinely helps.

## The mechanism `registerCustomEditor(...)` on `DataBinder` (and on `BeanWrapper`, both via `PropertyEditorRegistry`) registers a **JavaBeans `java.beans.PropertyEditor`** — a small strategy object that converts between a `String` and some target type. A `PropertyEditor` you care about implements two methods: - `setAsText(String text)` — parse the incoming text and store the typed value. - `getAsText()` — render the current value back to a String (for redisplay). Most custom editors extend `java.beans.PropertyEditorSupport` so you only override what you need. ```java binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false)); ``` Now any bound String targeting a `Date` property is parsed with that format. ## Overloads / scoping - `registerCustomEditor(Class<?> requiredType, PropertyEditor editor)` — applies to **all** properties of that type. - `registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor editor)` — applies **only** to the named property path (e.g. `"startDate"`, or nested `"event.startDate"`). The path form lets two `Date` fields use different formats. ## Spring-provided editors Spring includes many ready-made ones in `org.springframework.beans.propertyeditors`: `CustomDateEditor`, `CustomNumberEditor`, `StringTrimmerEditor` (trims/whitespace-to-null), `CustomCollectionEditor`, `CustomBooleanEditor`, etc. Some default editors are registered automatically. ## Where you register them in MVC The idiomatic place is an `@InitBinder` method in a `@Controller`: ```java @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true)); binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); // empty -> null } ``` This runs per request, configuring that request's binder before `@ModelAttribute` binding. ## Thread-safety gotcha `PropertyEditor` implementations are **stateful and not thread-safe** (they hold the value being edited). That's exactly why registration is per-binder / per-request rather than a shared singleton — never register one editor instance in a way that lets concurrent requests share it. ## registerCustomEditor vs ConversionService Historically PropertyEditors were the only conversion SPI. Spring 3 introduced the `core.convert` `ConversionService` with `Converter`/`Formatter` — stateless, thread-safe, and container-wide. **How values are actually converted, and Formatter/Converter, belong to the Type Conversion leaf.** For this topic, the point is: `registerCustomEditor` is the DataBinder/BeanWrapper hook for **per-binder, field-scoped, stateful** conversion, still useful for legacy editors or property-specific parsing, and it coexists with a configured ConversionService. ## When to use - Field-specific parsing where two properties of the same type need different formats (use the path-scoped overload). - Reusing an existing `PropertyEditor`. - Simple trimming/normalization (`StringTrimmerEditor`). Otherwise prefer a `Formatter` registered on the shared `ConversionService`.

  • Why are PropertyEditors registered per-binder rather than as a shared singleton?
    Because a PropertyEditor holds the value being edited as instance state and is not thread-safe; sharing one instance across concurrent requests would corrupt data. @InitBinder creates fresh editors per request.
  • How would you make two Date fields on the same object use different date formats?
    Use the path-scoped overload registerCustomEditor(Date.class, "startDate", editorA) and registerCustomEditor(Date.class, "endDate", editorB) so each editor is bound to a specific property path.

saying these in an interview costs you the question

  • Saying PropertyEditors are thread-safe and can be shared as singletons
  • Claiming registerCustomEditor and ConversionService cannot coexist
  • Thinking @InitBinder editors are global to the app rather than per-controller/per-request

context