When would you use addViewControllers and addFormatters, and what do they do?
answer
- viewController: URL→view, no controller (login, home)
- also addRedirectViewController / addStatusController
- addFormatters → ConversionService for data binding
- Converter=type→type, Formatter=locale-aware String↔object
- affects params/path/form NOT @RequestBody JSON
basics
~20 saddViewControllers 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 saddViewControllers(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@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
Should recognize addViewController maps a URL to a view without a controller.
Should explain both callbacks and give a concrete conversion example.
Should distinguish Converter vs Formatter and know formatters affect binding not @RequestBody.
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