skip to content

What is MessageHeaderAccessor and how does it relate to MessageBuilder and the standard headers?

level: seniorimportance: should knowfreq 45%

answer

  1. mutable accessor → toMessage() → immutable
  2. typed setters: replyChannel, contentType, errorChannel
  3. constants on MessageHeaders (ID/TIMESTAMP/REPLY_CHANNEL)
  4. SimpMessageHeaderAccessor for STOMP
  5. controls id/timestamp generation

basics

~20 s

MessageHeaderAccessor is a mutable, typed wrapper for building/reading headers. You mutate it freely, then call toMessage() (or hand it to MessageBuilder) to produce an immutable message. It also exposes typed getters for standard headers like id, timestamp, replyChannel, and contentType.

solid answer

~40 s

org.springframework.messaging.support.MessageHeaderAccessor is a mutable staging object for headers, contrasted with the immutable MessageHeaders. You set/get headers on it with type-aware convenience methods (setReplyChannel, setErrorChannel, setContentType, getId, getTimestamp), then call toMessage(payload) to snapshot it into an immutable GenericMessage. MessageBuilder can consume one via MessageBuilder.withPayload(p).setHeaders(accessor). Standard headers live as constants on MessageHeaders: ID, TIMESTAMP, REPLY_CHANNEL, ERROR_CHANNEL, CONTENT_TYPE. Subclasses specialize it — SimpMessageHeaderAccessor for STOMP/WebSocket, NativeMessageHeaderAccessor for protocol-native headers. A key advanced feature: you can disable automatic id and timestamp generation via setIdGenerator / disabling, and you can mark an accessor as mutable and reuse it across processing steps for performance, then leave it immutable for the final message. It centralizes header logic instead of scattering raw string keys.

code

java · 15 lines
java
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.MimeTypeUtils;

MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setContentType(MimeTypeUtils.APPLICATION_JSON);
accessor.setReplyChannelName("responses");
accessor.setHeader("orderId", 42L);

// Snapshot into an immutable message
Message<String> msg = accessor.toMessage("{\"id\":123}");

// Typed reads of standard headers
Object id = accessor.getId();          // UUID
Object ts = accessor.getTimestamp();   // Long epoch millis

go deeper

for a junior

Aware it's a helper for headers; not expected in depth.

for a middle

Know toMessage() and typed setters for contentType/replyChannel.

for a senior

Explain the mutable-accessor-to-immutable-message lifecycle and standard-header constants.

for a principal

Discuss id/timestamp generator control, mutability optimization, and protocol-specific subclasses.

## The problem it solves `MessageHeaders` is immutable, and raw `setHeader("someKey", val)` calls scatter untyped string keys everywhere. **`MessageHeaderAccessor`** (`org.springframework.messaging.support.MessageHeaderAccessor`) is a **mutable, typed façade** you use while assembling or inspecting headers, before snapshotting them into an immutable `MessageHeaders`. ## Core lifecycle ```java MessageHeaderAccessor accessor = new MessageHeaderAccessor(); accessor.setHeader("orderId", 42L); accessor.setContentType(MimeTypeUtils.APPLICATION_JSON); accessor.setReplyChannelName("responses"); Message<String> msg = accessor.toMessage("order-123"); ``` - Mutate freely on the accessor (it's a builder-like scratchpad). - `toMessage(payload)` produces an **immutable** `GenericMessage` snapshot. - Alternatively feed it to a builder: `MessageBuilder.withPayload(p).setHeaders(accessor).build()`. ## Typed access to standard headers Instead of remembering string keys, the accessor exposes typed getters/setters that map to the constants on `MessageHeaders`: | Constant | Accessor method | Meaning | |---|---|---| | `MessageHeaders.ID` | `getId()` | per-message UUID | | `MessageHeaders.TIMESTAMP` | `getTimestamp()` | creation epoch millis | | `MessageHeaders.REPLY_CHANNEL` | `setReplyChannelName` / `setReplyChannel` | where a reply should go (request/reply) | | `MessageHeaders.ERROR_CHANNEL` | `setErrorChannelName` / `setErrorChannel` | where errors should be routed | | `MessageHeaders.CONTENT_TYPE` | `setContentType` | payload MIME type | The **reply-channel** header is the mechanism behind request/reply: a producer sets it so the framework knows where to send the response; it can be a `MessageChannel` object or a String name resolved against the registry. ## Reading headers from an existing message `MessageHeaderAccessor.getAccessor(message, SomeAccessor.class)` (or `getMutableAccessor`) pulls a typed accessor back out of a received message so you can inspect protocol-specific headers cleanly. ## ID / timestamp control By default a new `MessageHeaders` gets a UUID `id` and a `timestamp`. Generation is governed by an `IdGenerator`; for high-throughput or when you want to preserve identity across copies you can configure/disable it. This is exactly why `MessageBuilder.fromMessage(...).build()` normally *regenerates* id/timestamp — the fresh headers get new values unless suppressed. ## Specialized subclasses - `SimpMessageHeaderAccessor` — STOMP-over-WebSocket messaging (destination, session id, subscription id). - `NativeMessageHeaderAccessor` — carries a nested map of protocol-native headers separate from Spring headers. - `IntegrationMessageHeaderAccessor` (Spring Integration) — correlation id, sequence number/size for aggregator/splitter. ## Mutability optimization An accessor can be marked mutable and threaded through several processing steps to avoid repeatedly copying header maps, then the final `toMessage` yields the immutable result. This is an internal performance path — application code usually just builds once. ## Gotchas - The accessor is **not** the message — reading `accessor.getMessageHeaders()` gives a live view; the immutability guarantee only applies to the message you finally produce. - Don't hand-set the `id`/`timestamp` string keys expecting them to survive — the framework manages those; use the generator configuration instead. - Reply-channel resolution by name requires the channel to be registered/resolvable, otherwise dispatch fails at send time.

  • What is the reply-channel header used for?
    It tells the framework where to send the response in a request/reply exchange; it can hold a MessageChannel instance or a String name resolved against the channel registry.
  • Name a specialized MessageHeaderAccessor subclass and its use.
    SimpMessageHeaderAccessor for STOMP/WebSocket messaging (destination, session/subscription ids); or NativeMessageHeaderAccessor for protocol-native headers.

saying these in an interview costs you the question

  • Saying MessageHeaderAccessor is itself immutable
  • Confusing it with MessageBuilder (accessor is the mutable header façade; builder assembles the whole message)
  • Claiming you set id/timestamp manually via string keys

context