skip to content

When would you use addViewControllers and addFormatters, and what do they do?

level: middleimportance: should knowfreq 40%

answer

  1. viewController: URL→view, no controller (login, home)
  2. also addRedirectViewController / addStatusController
  3. addFormatters → ConversionService for data binding
  4. Converter=type→type, Formatter=locale-aware String↔object
  5. affects params/path/form NOT @RequestBody JSON

basics

~20 s

addViewControllers maps a URL straight to a view name (or redirect/status) with no controller code — good for simple pages like /login. addFormatters registers Converters/Formatters that turn request strings into typed method arguments, e.g. String to LocalDate.

solid answer

~40 s

addViewControllers(ViewControllerRegistry) lets you declare pure view mappings without writing a @Controller — registry.addViewController("/login").setViewName("login"), or addRedirectViewController and addStatusController. It's for static/parameterless pages where a handler method would just return a view name. addFormatters(FormatterRegistry) registers Converter<S,T>, ConverterFactory, Formatter<T>, or a Printer/Parser into the MVC ConversionService. These drive type conversion during data binding — converting request params, path variables, and form fields (Strings) into typed controller arguments, e.g. String to LocalDate, String to an enum, or String id to a loaded entity. Formatters are locale-aware (parse/print) whereas Converters are plain type-to-type. In Boot many common conversions already exist; you add formatters for custom types. Both are startup-time infrastructure config exposed as WebMvcConfigurer callbacks.

code

java · 19 lines
java
@Configuration
public class UiConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addRedirectViewController("/home", "/");
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // String "2026-07-22" -> LocalDate for @RequestParam/@PathVariable
        registry.addFormatter(new DateTimeFormatterRegistrar() {{
            setDateFormatter(DateTimeFormatter.ISO_LOCAL_DATE);
        }}.getDateFormatter() == null ? new DateFormatter("yyyy-MM-dd")
                                       : new DateFormatter("yyyy-MM-dd"));
        registry.addConverter(new StringToProductCodeConverter());
    }
}

go deeper

for a junior

Should recognize addViewController maps a URL to a view without a controller.

for a middle

Should explain both callbacks and give a concrete conversion example.

for a senior

Should distinguish Converter vs Formatter and know formatters affect binding not @RequestBody.

for a principal

Should guide when server-rendered view mappings are appropriate vs a REST/SPA split and standardize conversion strategy.

## addViewControllers `addViewControllers(ViewControllerRegistry registry)` registers **parameterless URL→view mappings** so you don't write a boilerplate controller whose only job is `return "someView";`. ```java @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); registry.addViewController("/").setViewName("home"); registry.addRedirectViewController("/old", "/new"); registry.addStatusController("/teapot", HttpStatus.I_AM_A_TEAPOT); } ``` - `addViewController(path).setViewName(view)` — renders a view via the configured `ViewResolver` (Thymeleaf, JSP, etc.). - `addRedirectViewController(from, to)` — issues a redirect. - `addStatusController(path, status)` — returns a bare status code. - You can set an order via `registry.setOrder(...)` relative to other handler mappings. Use it for static landing/login/error pages in server-rendered apps. Not relevant to pure REST/JSON APIs. ## addFormatters `addFormatters(FormatterRegistry registry)` plugs custom **type-conversion** logic into the MVC `ConversionService` used during **data binding** — the process where Spring maps request Strings (`@RequestParam`, `@PathVariable`, form fields on a `@ModelAttribute`) to typed handler-method parameters. What you can register: - **`Converter<S, T>`** — one-way `S → T` (e.g. `String → ProductCode`). - **`ConverterFactory<S, R>`** — family of converters (e.g. String → any enum). - **`Formatter<T>`** — locale-aware `parse(String, Locale)` and `print(T, Locale)`; ideal for dates, numbers, currencies whose textual form depends on locale. - **`Printer<T>` / `Parser<T>`** — the two halves of a Formatter. ```java @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToProductCodeConverter()); registry.addFormatter(new DateFormatter("yyyy-MM-dd")); } ``` ### Converter vs Formatter - **Converter**: generic type-to-type, no locale, usable anywhere in Spring's core conversion. - **Formatter**: String↔Object with `Locale`, specifically for UI/web field formatting. ### Relationship to defaults Spring/Boot preregister many converters (primitives, enums, common `java.time` types when `spring.mvc.format.*` or annotations like `@DateTimeFormat` are used). You add formatters for **domain-specific** conversions (e.g. String id → loaded entity via a repository lookup) or custom textual formats. ### Gotcha Adding formatters here affects **web data binding**, not `@RequestBody` deserialization — that path goes through message converters (Jackson), not the ConversionService. So a formatter won't change how a JSON field is parsed; it changes how a query param / form field / path variable is parsed. ## When to use each - `addViewControllers`: server-side view app needs a no-logic page mapping. - `addFormatters`: you need custom or domain type conversion for request params/path variables/form binding.

  • Will a Formatter registered via addFormatters change how a JSON @RequestBody field is parsed?
    No. @RequestBody goes through HttpMessageConverters (Jackson), not the MVC ConversionService. Formatters affect @RequestParam, @PathVariable, and form/@ModelAttribute binding only.
  • What's the difference between a Converter and a Formatter?
    A Converter is a generic one-way S→T type conversion with no locale. A Formatter is a locale-aware String↔Object pair (parse/print) intended for web/UI field formatting, e.g. dates and numbers.

saying these in an interview costs you the question

  • Thinking addViewControllers can carry business logic or model data (it can't beyond a static view/redirect/status)
  • Believing addFormatters affects @RequestBody JSON parsing
  • Confusing Converter (no locale) with Formatter (locale-aware)
  • Writing an empty controller when addViewController would do

context