In Spring WebFlux, what are Encoder/Decoder and HttpMessageReader/HttpMessageWriter, and what do they do?
answer
- Encoder=objects->bytes, Decoder=bytes->objects
- Reader/Writer add HTTP concerns
- EncoderHttpMessageWriter wraps Encoder
- Everything is Flux<DataBuffer>
- Jackson2JsonEncoder/Decoder = JSON pair
basics
~10 sThey convert between Java objects and the raw bytes of an HTTP body. A Decoder/HttpMessageReader turns request bytes into objects; an Encoder/HttpMessageWriter turns response objects back into bytes (e.g. JSON).
solid answer
~40 sWebFlux replaces MVC's HttpMessageConverter with two reactive abstractions. Encoder<T> serializes a stream of objects into bytes; Decoder<T> deserializes bytes into objects. These are content-only, unaware of HTTP. HttpMessageWriter and HttpMessageReader wrap them and add HTTP concerns like Content-Type negotiation and headers. The standard adapters are EncoderHttpMessageWriter and DecoderHttpMessageReader, so registering a codec usually means registering an Encoder/Decoder pair. Everything works on Reactive Streams Publishers (Flux/Mono) of DataBuffer rather than blocking byte arrays, so bodies can be processed as they stream in and out. Jackson2JsonEncoder/Jackson2JsonDecoder are the built-in JSON pair; there are also codecs for plain strings, form data, multipart, and byte buffers.
code
java · 19 lines// Content-only layer
public interface Encoder<T> {
Flux<DataBuffer> encode(Publisher<? extends T> inputStream,
DataBufferFactory bufferFactory,
ResolvableType elementType,
MimeType mimeType,
Map<String, Object> hints);
}
public interface Decoder<T> {
Flux<T> decode(Publisher<DataBuffer> inputStream,
ResolvableType elementType,
MimeType mimeType,
Map<String, Object> hints);
}
// HTTP-aware wrappers used at runtime:
// EncoderHttpMessageWriter wraps an Encoder<T>
// DecoderHttpMessageReader wraps a Decoder<T>go deeper
Know the direction: Decoder/Reader = incoming bytes to objects, Encoder/Writer = objects to outgoing bytes, and that JSON uses Jackson.
Explain the two layers (content Encoder/Decoder vs HTTP Reader/Writer) and that they stream DataBuffers reactively.
Contrast with MVC's HttpMessageConverter and explain MIME/type-based selection plus the EncoderHttpMessageWriter/DecoderHttpMessageReader wrapping.
Discuss streaming vs aggregation semantics, reference-counted DataBuffer lifecycle, and how codec selection interacts with content negotiation across server and WebClient.
## The problem they solve An HTTP body on the wire is just bytes. A controller wants to work with Java objects (a `User`, a `List<Order>`). Something must translate between the two. In blocking **Spring MVC** that job belongs to `HttpMessageConverter`. In reactive **Spring WebFlux** the same job is split across a small layered set of interfaces designed to work with streaming, non-blocking data. ## The two layers **1. Content codecs (HTTP-agnostic):** - `Encoder<T>` — takes a `Publisher<? extends T>` (a stream of objects) and produces a `Flux<DataBuffer>` (a stream of byte chunks). Used to write responses / request bodies. - `Decoder<T>` — takes a `Publisher<DataBuffer>` and produces a `Flux<T>` (objects). Used to read request / response bodies. These know nothing about HTTP headers or status codes — only how to turn objects into bytes and back for a given `MimeType`. **2. Message codecs (HTTP-aware):** - `HttpMessageWriter<T>` — writes a `Publisher<T>` to a `ReactiveHttpOutputMessage`, choosing the `Content-Type`, setting `Content-Length`, etc. - `HttpMessageReader<T>` — reads a `ReactiveHttpInputMessage` into a `Flux<T>` / `Mono<T>`, checking the request's `Content-Type`. The glue: `EncoderHttpMessageWriter` wraps an `Encoder`, and `DecoderHttpMessageReader` wraps a `Decoder`. So most of the time you supply an `Encoder`/`Decoder` and Spring wraps it into the HTTP-aware layer automatically. ## DataBuffer `DataBuffer` is Spring's abstraction over a chunk of bytes (it wraps a Netty `ByteBuf` or a `java.nio.ByteBuffer` depending on the runtime). Because bodies are streams of `DataBuffer`, WebFlux can process data incrementally without buffering the whole payload into a `byte[]` — the key difference from MVC. ## Built-in codecs - **JSON:** `Jackson2JsonEncoder` / `Jackson2JsonDecoder` (Jackson-backed). - **Strings:** `CharSequenceEncoder` / `StringDecoder`. - **Bytes / resources:** `ByteArrayEncoder`, `ByteBufferEncoder`, `DataBufferEncoder`, `ResourceEncoder`/`ResourceDecoder`. - **Form & multipart:** `FormHttpMessageReader`/`Writer`, multipart readers. - (Optional) protobuf, Jackson Smile, XML (JAXB/Jackson) codecs. ## Where they live On the server the active set is held by a `ServerCodecConfigurer`; on the client (`WebClient`) by a `ClientCodecConfigurer`. You customize them via `WebFluxConfigurer.configureHttpMessageCodecs(...)` or `WebClient.Builder.codecs(...)`. ## Gotchas - Codecs are selected by **MIME type** and target **type** — if none matches you get `415 Unsupported Media Type` (read) or `406 Not Acceptable` / no writer found. - With streaming JSON of multiple elements, WebFlux uses **NDJSON / `application/stream+json`** semantics for true streaming; a plain `application/json` array is aggregated. - Because Netty `DataBuffer`s are reference-counted, low-level custom decoders must **release** buffers (`DataBufferUtils.release`) to avoid leaks — the built-in codecs handle this for you.
- What is the equivalent abstraction in Spring MVC, and why can't WebFlux reuse it?Spring MVC uses HttpMessageConverter, which reads/writes the whole body from blocking InputStream/OutputStream (byte[]). WebFlux is non-blocking and stream-based, so it needs codecs that operate on Publisher<DataBuffer> instead.
- How does WebFlux decide which codec to use for a given request or response?By matching the message's Content-Type (or the request's Accept header for writing) against each codec's supported MimeTypes, plus checking the target Java type it can handle. First matching reader/writer wins; no match yields 415 or 406.
saying these in an interview costs you the question
- Saying WebFlux uses HttpMessageConverter like MVC
- Claiming Encoder/Decoder handle HTTP headers/status (that's the Reader/Writer layer)
- Thinking bodies are always buffered into a byte[] before decoding