What is an HttpMessageConverter in Spring MVC, and when does Spring use one?
answer
- body <-> object SPI
- @RequestBody read / @ResponseBody write
- canRead/canWrite = type + media type
- conditional on classpath (Jackson=JSON)
- view-returning @Controller does NOT use converters
basics
~10 sIt converts between HTTP request/response bodies (bytes) and Java objects. Spring uses it whenever you annotate a parameter with @RequestBody or a return value with @ResponseBody (including @RestController).
solid answer
~40 sAn HttpMessageConverter is Spring's SPI for turning an HTTP message body into a Java object (read) and a Java object back into a body (write). It powers @RequestBody, @ResponseBody, HttpEntity, and ResponseEntity — everything a @RestController produces or consumes. Each converter declares which Java types and media types it handles via canRead(Class, MediaType) and canWrite(Class, MediaType). For example, MappingJackson2HttpMessageConverter maps JSON <-> objects, StringHttpMessageConverter handles plain text, and ByteArrayHttpMessageConverter handles byte[]. When a controller method returns an object, Spring picks the first registered converter whose canWrite matches both the object's type and the negotiated response content type, then calls write() to serialize it. Reading works symmetrically using the request's Content-Type.
code
java · 20 lines@RestController
public class UserController {
// @RequestBody: MappingJackson2HttpMessageConverter READS the JSON body
// into a UserDto (request Content-Type: application/json).
// Return value: the same converter WRITES the UserDto back as JSON
// because @RestController implies @ResponseBody and the client
// sent Accept: application/json.
@PostMapping("/users")
public UserDto create(@RequestBody UserDto in) {
return in.withId(42L);
}
// Returns raw text -> StringHttpMessageConverter writes text/plain,
// NOT JSON. A common surprise.
@GetMapping("/ping")
public String ping() {
return "pong";
}
}go deeper
Know it converts body <-> object and is triggered by @RequestBody / @ResponseBody.
Name the main built-in converters and know they're registered conditionally on the classpath.
Explain the read/write SPI (canRead/canWrite by type + media type) and that view returns bypass converters.
Contrast message conversion (whole body) with Converter/Formatter (single value), and understand the argument-resolver / return-value-handler machinery that drives it.
## What it is `HttpMessageConverter<T>` is a strategy interface (an SPI — service provider interface) in `org.springframework.http.converter`. Its job is **body serialization**: converting the raw bytes of an HTTP message body to and from Java objects. It is distinct from `Converter`/`Formatter` (which convert individual values like a String path variable to an `int`) — an `HttpMessageConverter` deals with the **whole body** of a request or response. ## The interface ```java public interface HttpMessageConverter<T> { boolean canRead(Class<?> clazz, MediaType mediaType); boolean canWrite(Class<?> clazz, MediaType mediaType); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> clazz, HttpInputMessage inputMessage); void write(T t, MediaType contentType, HttpOutputMessage outputMessage); } ``` - **read** = deserialize the request body into a Java object. - **write** = serialize a Java object into the response body. - **canRead / canWrite** = ask the converter whether it can handle a given Java type AND a given media type (MIME type such as `application/json`). ## When Spring invokes them Converters are triggered by the argument resolvers / return-value handlers for these annotations and types: - `@RequestBody` on a parameter → **read** the request body into that parameter (handled by `RequestResponseBodyMethodProcessor`). - `@ResponseBody` on a method / class-level `@RestController` → **write** the return value as the response body. - `HttpEntity<T>` parameter → read; `ResponseEntity<T>` / `HttpEntity<T>` return → write (handled by `HttpEntityMethodProcessor`). Plain `@Controller` methods that return a **view name** do NOT use message converters — those go through the view resolution / `ViewResolver` path instead. Message conversion is specifically the `@RestController` / REST-body path. ## Default converters Spring MVC registers a default list (via `WebMvcConfigurationSupport.addDefaultHttpMessageConverters`). Common ones: - `ByteArrayHttpMessageConverter` — `byte[]` - `StringHttpMessageConverter` — `String` (text) - `ResourceHttpMessageConverter` / `ResourceRegionHttpMessageConverter` — `Resource` (files, range requests) - `AllEncompassingFormHttpMessageConverter` — form data (`application/x-www-form-urlencoded`, `multipart/form-data`) - `MappingJackson2HttpMessageConverter` — JSON (only if Jackson is on the classpath) Many are registered **conditionally** based on what libraries are present on the classpath (Jackson for JSON, JAXB for XML, etc.). ## Gotcha Because selection is media-type + type based, forgetting a `Content-Type` header on a request or an `Accept` header on the client can change which converter is chosen. And returning a raw `String` from a `@RestController` produces `text/plain` via `StringHttpMessageConverter`, not JSON — a frequent surprise.
- Does a @Controller method returning a view name use an HttpMessageConverter?No. View-name returns go through view resolution (ViewResolver). Message converters are used only for body-producing paths: @ResponseBody / @RestController / ResponseEntity / HttpEntity.
- Why might a @RestController method return text/plain instead of JSON?Because it returns a raw String, and StringHttpMessageConverter (which handles String + text/plain and */*) is selected before or instead of Jackson. To force JSON, wrap the value in an object/DTO or set produces = application/json.