skip to content

How do you register a custom Encoder/Decoder or tune the codec set via CodecConfigurer, and how do default vs custom codecs interact?

level: principalimportance: nice to knowfreq 25%

answer

  1. defaultCodecs() tunes built-ins; customCodecs() adds yours
  2. registerWithDefaultConfig inherits maxInMemorySize
  3. configureHttpMessageCodecs / WebClient.codecs / CodecCustomizer
  4. First-match by MIME + target type; watch shadowing
  5. Prefer new Jackson2JsonEncoder(mapper) over raw DataBuffer code

basics

~10 s

Implement Encoder/Decoder (or HttpMessageReader/Writer), then register it in CodecConfigurer via customCodecs().register(...). Use defaultCodecs() to tweak built-ins (like maxInMemorySize or the Jackson mapper). Do this in WebFluxConfigurer.configureHttpMessageCodecs on the server or WebClient.Builder.codecs.

solid answer

~40 s

CodecConfigurer splits into defaultCodecs() and customCodecs(). defaultCodecs() configures the built-in set — replace the Jackson encoder/decoder, set maxInMemorySize, toggle logging. customCodecs().register(...) or registerWithDefaultConfig(...) adds your own Encoder/Decoder/HttpMessageWriter/HttpMessageReader; registerWithDefaultConfig means your codec inherits shared settings like maxInMemorySize. Ordering matters: custom codecs are placed so they can take precedence for their MIME/type before defaults, and reader/writer selection is first-match by supported media type and target type. You apply this on the server through WebFluxConfigurer.configureHttpMessageCodecs(ServerCodecConfigurer) and on the client via WebClient.Builder.codecs(Consumer<ClientCodecConfigurer>). In Spring Boot, a CodecCustomizer bean lets you adjust codecs globally for both server and WebClient without re-implementing the configurer. Prefer reusing an existing Encoder/Decoder (e.g. a custom-ObjectMapper Jackson codec) over hand-writing DataBuffer plumbing.

code

java · 24 lines
java
@Configuration
public class CustomCodecConfig implements WebFluxConfigurer {

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        // 1) Tune a built-in
        configurer.defaultCodecs().maxInMemorySize(1024 * 1024);

        // 2) Replace JSON codecs with a custom ObjectMapper (reuse, no DataBuffer code)
        ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
        configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper));
        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper));

        // 3) Add a fully custom codec, inheriting shared config (maxInMemorySize, logging)
        configurer.customCodecs()
                .registerWithDefaultConfig(new CsvEncoder());   // implements Encoder<MyRow>
    }
}

// Spring Boot alternative: one bean tunes server + WebClient default codecs
@Bean
CodecCustomizer globalCodecs() {
    return configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1024);
}

go deeper

for a junior

Know that you can add/tune codecs in configureHttpMessageCodecs, but details are beyond junior scope.

for a middle

Explain defaultCodecs() vs customCodecs() and where you call configureHttpMessageCodecs / WebClient.codecs.

for a senior

Cover registerWithDefaultConfig inheriting maxInMemorySize, first-match ordering, and preferring ObjectMapper reuse over raw codecs.

for a principal

Reason about shadowing risk, server/client consistency via CodecCustomizer, streaming-vs-single-value codec design, and buffering/DoS implications of registration choices.

## The CodecConfigurer surface `CodecConfigurer` (server: `ServerCodecConfigurer`, client: `ClientCodecConfigurer`) is the registry of active readers/writers. It exposes two groups: **1. `defaultCodecs()` — configure the built-ins.** Methods include: - `maxInMemorySize(int)` — buffering cap for all aggregating default codecs. - `jackson2JsonEncoder(Encoder)` / `jackson2JsonDecoder(Decoder)` — replace the JSON codecs (e.g. with a custom `ObjectMapper`). - `enableLoggingRequestDetails(boolean)` — whether to log potentially sensitive body/form/header details. - protobuf, Jackson Smile, and other type-specific setters. These don't add new codecs; they tune the ones Spring already registers. **2. `customCodecs()` — add your own.** Methods: - `register(Object codec)` — add an `Encoder`, `Decoder`, `HttpMessageReader`, or `HttpMessageWriter`. It uses its own configuration. - `registerWithDefaultConfig(Object codec)` — add it **and** apply the shared defaults (notably `maxInMemorySize` and logging) so your codec behaves consistently with the built-ins. ## Where you call it - **Server:** implement `WebFluxConfigurer` and override `configureHttpMessageCodecs(ServerCodecConfigurer configurer)`. - **WebClient:** `WebClient.builder().codecs(Consumer<ClientCodecConfigurer>)`. - **Spring Boot global:** define a `CodecCustomizer` bean — Boot applies it to both the server's `ServerCodecConfigurer` and `WebClient` default codecs, so you tune once. ## Ordering and selection At runtime, reading/writing picks the **first** reader/writer whose supported MIME types and target Java type match. `CodecConfigurer` arranges the list so that: - typed/custom codecs generally get a chance before broad catch-alls, - default codecs remain available as fallbacks. So a custom codec for `application/vnd.myco+json` can intercept that media type while the standard Jackson codec still handles plain `application/json`. If two codecs both claim a type, order determines the winner — a subtle source of bugs when a custom codec unexpectedly shadows a default. ## Implementing a codec: two levels 1. **Reuse.** The common, recommended case: wrap an existing codec with different config — e.g. `new Jackson2JsonEncoder(myMapper)`. No `DataBuffer` handling required. 2. **From scratch.** Implement `Encoder<T>`/`Decoder<T>` (or extend helpers like `AbstractDataBufferDecoder`, `AbstractSingleValueEncoder`). Here you own `DataBuffer` lifecycle — allocate via the provided `DataBufferFactory`, and **release** input buffers (`DataBufferUtils.release`) to avoid native-memory leaks on Netty. You also declare `canEncode`/`canDecode` and the supported `MimeType`s. ## Design guidance (principal lens) - **Prefer configuration over new codecs.** Most needs (date formats, naming strategies, unknown-property tolerance) are ObjectMapper concerns — solve them there, not with a bespoke codec. - **Use `registerWithDefaultConfig`** so custom codecs inherit `maxInMemorySize`; forgetting this can leave a codec with unbounded buffering — a DoS gap. - **Keep server and client consistent.** If custom media types cross the wire between your own services, register the codec on both `ServerCodecConfigurer` and the `WebClient` used to call peers, ideally via a shared `CodecCustomizer`. - **Beware shadowing.** Verify your custom codec's `canEncode`/`canDecode` is narrow enough not to hijack `application/json`. - **Streaming vs single-value.** Decide whether your codec supports element-by-element streaming (implement full `Encoder`) or only single values (`AbstractSingleValueEncoder`) — this affects whether `Flux<T>` bodies stream or aggregate. ## Summary Tune built-ins through `defaultCodecs()`, add new ones through `customCodecs().register[WithDefaultConfig]`, apply via `WebFluxConfigurer` / `WebClient` / a Boot `CodecCustomizer`, mind first-match ordering and the shared `maxInMemorySize`, and prefer reusing configured Jackson codecs over hand-rolling `DataBuffer` plumbing.

  • What is the difference between customCodecs().register(...) and registerWithDefaultConfig(...)?
    register(...) adds your codec with only its own configuration. registerWithDefaultConfig(...) also applies the configurer's shared defaults — chiefly maxInMemorySize and request-detail logging — so your codec inherits the same buffering cap and behavior as the built-ins, avoiding an unbounded-buffering gap.
  • How would you apply the same codec customization to both the WebFlux server and every WebClient in a Boot app?
    Define a single CodecCustomizer bean. Spring Boot applies registered CodecCustomizers to the server's ServerCodecConfigurer and to WebClient.Builder default codecs, so one bean keeps both sides consistent without re-implementing WebFluxConfigurer or per-builder .codecs calls.

saying these in an interview costs you the question

  • Hand-writing DataBuffer plumbing when a configured Jackson codec would do
  • Using register() and forgetting the custom codec then has no maxInMemorySize cap
  • Assuming registration order never affects which codec wins
  • Thinking defaultCodecs() adds new codecs rather than tuning existing ones

context