Spring Boot: Apache Kafka
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
This page assumes the baseline covered on Messaging with Apache
Kafka — spring.kafka. connection properties, the auto-configured KafkaTemplate and transactional
producers, @KafkaListener with manual/batch acknowledgment, DefaultErrorHandler with blocking retry and a
DeadLetterPublishingRecoverer, and the auto-configured ConsumerFactory/ProducerFactory with
JsonSerializer/JsonDeserializer — and does not repeat any of it. What follows is everything else Spring for
Apache Kafka 4.1 adds on top of that baseline: declarative topic provisioning; request/reply and multi-cluster
templates; per-listener acknowledgment mode; *non-blocking retry (as opposed to the blocking DefaultErrorHandler
retry already covered); rebalance listeners and manual seeking; pausing and resuming containers; the Spring
wrapper around Kafka’s share consumers (KIP-932); KafkaTransactionManager-driven exactly-once; Kafka Streams
via @EnableKafkaStreams; the Jackson-based serializers that replace JsonSerializer/JsonDeserializer; and
opting a listener container into the new KIP-848 consumer group protocol. Examples target Spring Boot 4.1.x,
Spring for Apache Kafka 4.1.x, and Apache Kafka 4.3.x, running on Java 25 (Java 21 is the current LTS both
target versions still support). For the protocol-level mechanics behind several of these features — share
groups, transactions, and Kafka Streams topologies — see
Kafka Share Groups,
Kafka Reliability and Exactly-Once Semantics, and
Kafka Streams respectively; this page only covers the Spring API that
wraps them.
Topic provisioning
An application that needs its topics to exist before the first producer send or consumer poll declares them as
NewTopic beans; Spring Boot auto-configures a KafkaAdmin bean from spring.kafka.bootstrap-servers that
finds every NewTopic bean in the context at startup and creates (or reconciles the partition/replica count of)
whichever ones do not already match:
@Configuration
public class OrderTopicsConfig {
@Bean
public NewTopic ordersPlaced() {
return TopicBuilder.name("orders.placed")
.partitions(6)
.replicas(3)
.config(TopicConfig.RETENTION_MS_CONFIG, String.valueOf(Duration.ofDays(7).toMillis()))
.build();
}
@Bean
public NewTopic ordersPlacedDlt() {
// .compact() switches the topic to log compaction instead of the default delete cleanup policy
return TopicBuilder.name("orders.placed.DLT")
.partitions(6)
.replicas(3)
.build();
}
}
TopicBuilder is a thin fluent front end over NewTopic; declaring several topics on one bean is possible with
KafkaAdmin.NewTopics, and KafkaAdmin itself exposes createOrModifyTopics(NewTopic…),
describeTopics(String…), and deleteTopics(String…) for provisioning done imperatively at runtime rather
than declaratively at startup. KafkaAdmin here plays the same startup-provisioning role Spring Boot’s own
KafkaAdmin auto-configuration already plays for the connection itself — no extra property is needed beyond
having NewTopic beans in the context. See
Spring for Apache Kafka reference — Configuring Topics.
Request/reply and multi-destination templates
ReplyingKafkaTemplate
KafkaTemplate (covered on the baseline page) is fire-and-forget: send() resolves once the broker
acknowledges the write, not once anything downstream has acted on it. ReplyingKafkaTemplate builds a
synchronous request/reply exchange on top of that same asynchronous transport, correlating each request with its
reply via a KafkaHeaders.CORRELATION_ID header:
@Configuration
public class OrderPricingRequestReplyConfig {
@Bean
public ReplyingKafkaTemplate<String, PriceRequest, PriceReply> replyingTemplate(
ProducerFactory<String, PriceRequest> producerFactory,
ConcurrentKafkaListenerContainerFactory<String, PriceReply> containerFactory) {
ConcurrentMessageListenerContainer<String, PriceReply> repliesContainer =
containerFactory.createContainer("pricing.replies");
repliesContainer.getContainerProperties().setGroupId("pricing-replies");
// the template itself starts/stops this container -- it must not auto-start on its own
repliesContainer.setAutoStartup(false);
return new ReplyingKafkaTemplate<>(producerFactory, repliesContainer);
}
}
@Service
public class OrderPricingClient {
private final ReplyingKafkaTemplate<String, PriceRequest, PriceReply> replyingTemplate;
public OrderPricingClient(ReplyingKafkaTemplate<String, PriceRequest, PriceReply> replyingTemplate) {
this.replyingTemplate = replyingTemplate;
}
public PriceReply priceOrder(String orderId, PriceRequest request) throws Exception {
ProducerRecord<String, PriceRequest> record = new ProducerRecord<>("pricing.requests", orderId, request);
RequestReplyFuture<String, PriceRequest, PriceReply> future =
replyingTemplate.sendAndReceive(record, Duration.ofSeconds(5));
future.getSendFuture().get(5, TimeUnit.SECONDS); // fail fast if the request itself never sent
return future.get(5, TimeUnit.SECONDS).value();
}
}
The pricing side replies by returning a value from a @KafkaListener annotated @SendTo, which Spring routes
back to the header-provided reply topic and correlation ID automatically:
@Component
public class OrderPricingServer {
@KafkaListener(id = "pricing-server", topics = "pricing.requests")
@SendTo
public PriceReply price(PriceRequest request) {
return pricingEngine.price(request);
}
}
RoutingKafkaTemplate
A single application that must publish to topics served by different clusters, or that need different value
serializers depending on the destination, routes through one RoutingKafkaTemplate instead of injecting several
KafkaTemplate beans and choosing between them by hand. It dispatches each send() to the first matching
ProducerFactory in an ordered map of topic-name regular expressions:
@Bean
public RoutingKafkaTemplate routingTemplate(
GenericApplicationContext context,
ProducerFactory<Object, Object> jsonProducerFactory) {
Map<String, Object> rawConfigs = new HashMap<>(jsonProducerFactory.getConfigurationProperties());
rawConfigs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
DefaultKafkaProducerFactory<Object, Object> rawProducerFactory =
new DefaultKafkaProducerFactory<>(rawConfigs);
context.registerBean("rawEventsProducerFactory", DefaultKafkaProducerFactory.class, () -> rawProducerFactory);
// ordered map: more specific patterns first, a catch-all default last
Map<Pattern, ProducerFactory<Object, Object>> routes = new LinkedHashMap<>();
routes.put(Pattern.compile("raw\\..+"), rawProducerFactory);
routes.put(Pattern.compile(".+"), jsonProducerFactory);
return new RoutingKafkaTemplate(routes);
}
A RoutingKafkaTemplate does not support transactions, execute(), flush(), or metrics(), because those
operations need a single, known producer/topic at configuration time, which a router by definition does not
have until the first send(). See
Spring for Apache Kafka reference — Sending Messages.
Per-listener acknowledgment mode
The baseline page sets acknowledgment mode globally, via spring.kafka.listener.ack-mode on the container
factory. Since Spring for Apache Kafka 4.1, the ackMode attribute on @KafkaListener itself overrides the
container factory’s default for that one listener, so a single factory can back listeners with different
acknowledgment needs instead of one factory bean per AckMode:
@Component
public class OrderEventListeners {
// container factory default ack mode applies here (e.g. BATCH, from application.yml)
@KafkaListener(id = "orders-placed", topics = "orders.placed")
public void onOrderPlaced(OrderPlacedEvent event) {
orderProcessor.process(event);
}
// this one listener needs to control its own commit point, without a dedicated factory bean
@KafkaListener(id = "orders-refunded", topics = "orders.refunded", ackMode = "MANUAL")
public void onOrderRefunded(OrderRefundedEvent event, Acknowledgment acknowledgment) {
refundProcessor.process(event);
acknowledgment.acknowledge();
}
}
ackMode accepts the same names as ContainerProperties.AckMode (RECORD, BATCH, MANUAL,
MANUAL_IMMEDIATE, and so on) and also accepts a ${…} placeholder or #{…} SpEL expression. See
Spring for
Apache Kafka reference — @KafkaListener Annotation.
Non-blocking retry with @RetryableTopic
The baseline page’s DefaultErrorHandler retries in place, blocking that partition’s poll loop between
attempts — fine for a fast, transient failure, but it stalls every other record behind the failing one for the
whole backoff period. @RetryableTopic instead retries by re-publishing the failed record to a dedicated
retry topic with its own consumer, so the original partition keeps moving while the retry waits in the
background:
@Component
public class OrderEventListener {
@RetryableTopic(
attempts = "4",
backoff = @Backoff(delay = 1000, multiplier = 2.0, maxDelay = 15000),
autoCreateTopics = "true",
exclude = IllegalArgumentException.class)
@KafkaListener(topics = "orders.placed", groupId = "orders-service")
public void onOrderPlaced(OrderPlacedEvent event) {
orderProcessor.process(event);
}
@DltHandler
public void onOrderPlacedDlt(OrderPlacedEvent event, @Header(KafkaHeaders.EXCEPTION_MESSAGE) String reason) {
log.error("order {} exhausted retries, sent to DLT: {}", event.orderId(), reason);
}
}
With the default topicSuffixingStrategy, each retry attempt gets its own physically separate topic — orders.placed-retry-0, orders.placed-retry-1, and so on — and a final failure lands on orders.placed.DLT,
the same .DLT suffix convention DeadLetterPublishingRecoverer uses on the baseline page. exclude (and its
counterpart include) keep exceptions that are never worth retrying — typically deserialization or validation
failures — from being retried at all, mirroring addNotRetryableExceptions on DefaultErrorHandler.
RetryTopicConfigurationBuilder is the equivalent programmatic, @Bean-based configuration for cases — such as
applying the same retry policy across many listeners — where annotating every method individually would be
repetitive. Non-blocking retry is not compatible with batch listeners or with container-managed transactions.
See Spring for Apache Kafka reference — Non-Blocking
Retries.
Rebalance listeners and seeking
ConsumerAwareRebalanceListener
A partition rebalance — a new consumer joining the group, an existing one leaving, a @RetryableTopic topic
being provisioned — is usually invisible to application code. When it must not be, ConsumerAwareRebalanceListener
hooks the moments before/after Spring commits any pending offsets on revocation and the moment new partitions are
assigned:
@Bean
public ConcurrentKafkaListenerContainerFactory<String, OrderPlacedEvent> ordersContainerFactory(
ConsumerFactory<String, OrderPlacedEvent> consumerFactory) {
ConcurrentKafkaListenerContainerFactory<String, OrderPlacedEvent> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
factory.getContainerProperties().setConsumerRebalanceListener(new ConsumerAwareRebalanceListener() {
@Override
public void onPartitionsRevokedAfterCommit(Consumer<?, ?> consumer, Collection<TopicPartition> partitions) {
partitions.forEach(tp -> offsetTracker.store(tp, consumer.position(tp)));
}
@Override
public void onPartitionsAssigned(Consumer<?, ?> consumer, Collection<TopicPartition> partitions) {
partitions.forEach(tp -> offsetTracker.restore(tp).ifPresent(offset -> consumer.seek(tp, offset)));
}
});
return factory;
}
onPartitionsRevokedBeforeCommit runs before Spring commits pending offsets (the place to flush any pending
Acknowledgment), onPartitionsRevokedAfterCommit runs once that commit has happened (the place to persist
state to an external store), and onPartitionsLost (since Spring for Apache Kafka 2.4) fires instead of the
revoked callbacks when the broker considers the assignment already gone. See
Spring for Apache
Kafka reference — Rebalance Listeners.
ConsumerSeekAware
A listener bean that needs to control its own starting offset — replaying from the beginning on a fresh
deployment, or seeking to a timestamp — implements ConsumerSeekAware and drives the ConsumerSeekCallback it
is handed:
@Component
public class OrderAuditListener extends AbstractConsumerSeekAware {
@KafkaListener(id = "orders-audit", topics = "orders.placed")
public void onOrderPlaced(OrderPlacedEvent event) {
auditLog.record(event);
}
@Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
// replay the last hour on every (re)assignment, instead of resuming from the committed offset
assignments.keySet().forEach(tp ->
callback.seekToTimestamp(tp.topic(), tp.partition(),
System.currentTimeMillis() - Duration.ofHours(1).toMillis()));
}
}
AbstractConsumerSeekAware (extended above) tracks the per-partition callback bookkeeping automatically; a
class implementing the raw ConsumerSeekAware interface directly must store the ConsumerSeekCallback itself
(typically per-thread) via registerSeekCallback and clean it up in onPartitionsRevoked. ConsumerSeekCallback
also exposes seek(topic, partition, offset), seekToBeginning/seekToEnd, and seekRelative for offsets
relative to the current position. See
Spring for Apache Kafka reference — Seeking to a
Specific Offset.
Pausing and resuming containers
A container backed by KafkaListenerEndpointRegistry can be paused and resumed by its listener ID, for example
to shed load while a downstream dependency is degraded:
@Service
public class DownstreamHealthGate {
private final KafkaListenerEndpointRegistry registry;
public DownstreamHealthGate(KafkaListenerEndpointRegistry registry) {
this.registry = registry;
}
public void onDownstreamUnhealthy() {
registry.getListenerContainer("orders-placed").pause();
}
public void onDownstreamHealthy() {
registry.getListenerContainer("orders-placed").resume();
}
}
pause() takes effect just before the container’s next poll() — it keeps polling with an empty subscription
to avoid triggering a rebalance, it simply stops returning records — and resume() takes effect right after the
poll that observes it; isPauseRequested()/isConsumerPaused() distinguish "asked to pause" from "actually
paused." ConsumerPausedEvent/ConsumerResumedEvent are published so other beans can react to the transition
via @EventListener instead of polling the container’s own state. See
Spring for Apache Kafka reference — Pausing and Resuming Listener Containers.
Share consumers: the Spring wrapper around Kafka queues
Kafka Share Groups covers the protocol underneath this section
in full — record-level acquisition locks, the ACCEPT/RELEASE/REJECT/RENEW outcomes, and delivery-count
poison-message handling. Spring for Apache Kafka 4.1 (backed by Kafka 4.2+ share-group support) wraps that
protocol behind the same @KafkaListener programming model already used for consumer groups, via a parallel set
of share-consumer-specific factories.
ShareConsumerFactory/DefaultShareConsumerFactory play the same role ConsumerFactory plays on the baseline
page, and ShareKafkaListenerContainerFactory is the share-consumer equivalent of
ConcurrentKafkaListenerContainerFactory:
@Configuration
public class OrderTaskShareConsumerConfig {
@Bean
public ShareConsumerFactory<String, OrderTaskEvent> orderTaskShareConsumerFactory(KafkaProperties properties) {
Map<String, Object> configs = properties.buildConsumerProperties(null);
return new DefaultShareConsumerFactory<>(configs);
}
@Bean
public ShareKafkaListenerContainerFactory<String, OrderTaskEvent> shareKafkaListenerContainerFactory(
ShareConsumerFactory<String, OrderTaskEvent> orderTaskShareConsumerFactory) {
ShareKafkaListenerContainerFactory<String, OrderTaskEvent> factory =
new ShareKafkaListenerContainerFactory<>(orderTaskShareConsumerFactory);
factory.getContainerProperties().setShareAckMode(ContainerProperties.ShareAckMode.MANUAL);
// a permanently-broken record is REJECTed instead of RELEASEd back for redelivery
factory.setShareConsumerRecordRecoverer((record, ex) ->
ex instanceof TransientTaskException ? AcknowledgeType.RELEASE : AcknowledgeType.REJECT);
factory.setConcurrency(5);
return factory;
}
}
@Component
public class OrderTaskShareListener {
@KafkaListener(
id = "order-task-workers",
topics = "orders.tasks",
groupId = "order-task-workers",
containerFactory = "shareKafkaListenerContainerFactory")
public void process(ConsumerRecord<String, OrderTaskEvent> record, ShareAcknowledgment acknowledgment) {
orderTaskProcessor.process(record.value());
acknowledgment.acknowledge(); // ACCEPT
}
}
ContainerProperties.ShareAckMode mirrors the protocol-level acknowledgement modes, plus a container-managed
option ShareAcknowledgment never appears in:
ShareAckMode |
Behavior |
|---|---|
|
The container itself acknowledges |
|
The listener drives every outcome itself through an injected |
|
The broker auto-accepts every acquired record regardless of processing outcome, matching the protocol’s own
|
ShareAcknowledgment exposes the same four outcomes as the plain-Java ShareConsumer example on
Kafka Share Groups — acknowledge() (ACCEPT), release()
(RELEASE), reject() (REJECT), and renew() (RENEW, extending the acquisition lock for a record still
being worked on) — and is only ever non-null in MANUAL mode. ShareConsumerRecordRecoverer is a functional
interface, (record, exception) → AcknowledgeType, that settles a listener exception in EXPLICIT mode without
the listener seeing an acknowledgment object at all; ShareConsumerRecordRecoverer.REJECTING and .RELEASING
are ready-made implementations for the two common policies.
As of Spring for Apache Kafka 4.1, share consumer support has real limitations beyond what the underlying
protocol already lacks (covered on Kafka Share Groups): no
message converters, no batch listeners, no partition assignment or topic-pattern subscription (both excluded by
the share-group protocol itself), no manual offset seeking, and error handling goes exclusively through
ShareConsumerRecordRecoverer rather than the CommonErrorHandler hierarchy used elsewhere on this page. See
Spring for Apache Kafka reference — Kafka
Queues (Share Consumers).
Exactly-once with KafkaTransactionManager
The baseline page shows transaction-id-prefix switching KafkaTemplate into transactional mode for a producer
calling @Transactional directly; Kafka
Reliability and Exactly-Once Semantics covers the underlying initTransactions()/beginTransaction()/
sendOffsetsToTransaction()/commitTransaction() protocol in full. The consume-transform-produce case — a @KafkaListener that both reads and writes Kafka inside the same transaction — is driven by a
KafkaTransactionManager the container uses, rather than by the listener method calling those APIs itself:
@Bean
public KafkaTransactionManager<Object, Object> kafkaTransactionManager(ProducerFactory<Object, Object> producerFactory) {
return new KafkaTransactionManager<>(producerFactory);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, OrderPlacedEvent> ordersContainerFactory(
ConsumerFactory<String, OrderPlacedEvent> consumerFactory,
KafkaTransactionManager<Object, Object> kafkaTransactionManager) {
ConcurrentKafkaListenerContainerFactory<String, OrderPlacedEvent> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
factory.getContainerProperties().setTransactionManager(kafkaTransactionManager);
return factory;
}
@Component
public class OrderEnrichmentListener {
private final KafkaTemplate<String, OrderEnrichedEvent> kafkaTemplate;
public OrderEnrichmentListener(KafkaTemplate<String, OrderEnrichedEvent> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
@KafkaListener(topics = "orders.placed", groupId = "order-enrichment")
public void onOrderPlaced(OrderPlacedEvent event) {
// the container already opened a Kafka transaction before this method ran;
// this send and the eventual offset commit join that same transaction automatically
kafkaTemplate.send("orders.enriched", event.orderId(), enrichmentService.enrich(event));
}
}
With KafkaTemplate’s `ProducerFactory set to the same one backing kafkaTransactionManager, the container
begins a transaction before invoking the listener, folds the consumed offset into it via
sendOffsetsToTransaction() on success, and commits; an exception aborts the transaction and repositions the
consumer to redeliver the rolled-back record(s) on the next poll — the read-process-write sequence is
exactly-once, while the read and process steps individually remain at-least-once. KafkaTemplate uses
KafkaAwareTransactionManager more broadly than just KafkaTransactionManager, but KafkaTransactionManager
is the concrete implementation used here. ChainedKafkaTransactionManager is deprecated; combining a Kafka
transaction with a separate resource (e.g. a database) now means letting the container’s
KafkaTransactionManager own the Kafka side and adding a plain @Transactional on the listener method for the
other resource’s own transaction manager. A downstream consumer that must only ever see committed output still
needs isolation-level: read_committed, exactly as on the baseline page:
spring:
kafka:
producer:
transaction-id-prefix: order-enrichment-tx-
consumer:
isolation-level: read_committed
Kafka Streams with @EnableKafkaStreams
Kafka Streams covers the topology/DSL concepts (KStream, KTable,
windowing, state stores) this section does not repeat. @EnableKafkaStreams on a @Configuration class wires
Spring’s own lifecycle around the native KafkaStreams client: it declares a StreamsBuilderFactoryBean (bean
name defaultKafkaStreamsBuilder) that starts and stops the underlying KafkaStreams instance along with the
Spring ApplicationContext, and makes a StreamsBuilder available for injection so topology can be built as
@Bean methods evaluated once, at context refresh, before the streams instance starts:
@Configuration
@EnableKafkaStreams
public class OrderStreamsConfig {
@Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
public KafkaStreamsConfiguration kafkaStreamsConfiguration(KafkaProperties properties) {
return new KafkaStreamsConfiguration(properties.buildStreamsProperties(null));
}
@Bean
public KStream<String, OrderPlacedEvent> ordersEnrichedStream(StreamsBuilder streamsBuilder) {
KStream<String, OrderPlacedEvent> stream = streamsBuilder.stream("orders.placed");
stream.mapValues(enrichmentService::enrich).to("orders.enriched");
return stream;
}
}
spring:
kafka:
streams:
application-id: order-enrichment-streams
bootstrap-servers: localhost:9092
properties:
errors.dead.letter.queue.topic.name: orders.enriched.DLT
Deserialization, processing, and production failures inside the topology each get their own recovering
exception handler since Spring for Apache Kafka 4.1 (Kafka Streams DLQ support, KIP-1034):
RecoveringDeserializationExceptionHandler, RecoveringProcessingExceptionHandler, and
RecoveringProductionExceptionHandler, configured via the matching StreamsConfig handler-class property, route
the offending record to a DeadLetterPublishingRecoverer (the same recoverer class the baseline page uses for
@KafkaListener) or to a custom KafkaStreamsDeadLetterDestinationResolver when different failure types need
different destinations:
@Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
public KafkaStreamsConfiguration kafkaStreamsConfiguration(KafkaProperties properties,
KafkaTemplate<Object, Object> kafkaTemplate) {
Map<String, Object> props = properties.buildStreamsProperties(null);
props.put(StreamsConfig.DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG,
RecoveringDeserializationExceptionHandler.class);
props.put(RecoveringDeserializationExceptionHandler.RECOVERER,
new DeadLetterPublishingRecoverer(kafkaTemplate,
(record, ex) -> new TopicPartition("orders.enriched.DLT", -1)));
return new KafkaStreamsConfiguration(props);
}
Jackson serializers and the new consumer group protocol
The baseline page’s spring.json.trusted.packages/spring.json.value.default.type examples target
JsonSerializer/JsonDeserializer. Spring for Apache Kafka’s Jackson 3-based replacements,
JacksonJsonSerializer/JacksonJsonDeserializer (same package,
org.springframework.kafka.support.serializer), use the equivalent static configuration constants instead of
raw property strings, and are the pair a new 4.1 application should reach for:
spring:
kafka:
producer:
value-serializer: org.springframework.kafka.support.serializer.JacksonJsonSerializer
consumer:
value-deserializer: org.springframework.kafka.support.serializer.JacksonJsonDeserializer
properties:
spring.json.trusted.packages: "com.example.orders.events"
spring.json.value.default.type: com.example.orders.events.OrderPlacedEvent
# opt this consumer group into the KIP-848 broker-side group protocol instead of the classic one
group.protocol: consumer
group.protocol: consumer (passed through under spring.kafka.consumer.properties, exactly like any other raw
ConsumerConfig entry the baseline page already shows) switches the consumer group onto Kafka’s newer,
broker-coordinated rebalance protocol instead of the classic client-side one; under it,
ConsumerAwareRebalanceListener.onPartitionsAssigned may fire more than once per rebalance, as partitions arrive
incrementally rather than all at once — code relying on a single call per rebalance (the seek example above
included) should tolerate being invoked repeatedly with smaller assignment sets rather than assuming
completeness on the first call. See
Spring for Apache Kafka reference — Serialization, Deserialization, and Message Conversion.
New spring.kafka.* properties introduced on this page
| Property | Purpose |
|---|---|
|
|
|
|
|
The Kafka Streams |
|
Overrides |
|
The default DLQ topic Kafka Streams' native exception handlers (KIP-1034) publish to when a
|
References
-
Spring for Apache Kafka reference —
@KafkaListenerAnnotation -
Spring for Apache Kafka reference — Seeking to a Specific Offset
-
Spring for Apache Kafka reference — Pausing and Resuming Listener Containers
-
Spring for Apache Kafka reference — Serialization, Deserialization, and Message Conversion
-
Spring for Apache Kafka reference — Kafka Queues (Share Consumers)