Spring for Apache Kafka 129 (https://github.com/spring-projects/spring-kafka 2,512 ) (Apache 2.0) wraps the Java client: KafkaTemplate sends, @KafkaListener methods receive, and listener containers own threads, commits and retries. Spring Boot 4.1.1 (20 August 2026) brings spring-kafka 4.1.1 and kafka-clients 4.2.1 through spring-boot-starter-kafka and configures both from application.yaml (here: bootstrap server, group id, earliest, group.protocol: consumer). BookNest's refunds service forwards every cancelled order to booknest.refund-requests:
@SpringBootApplication
public class RefundsApp {
private final KafkaTemplate<String, String> kafka;
RefundsApp(KafkaTemplate<String, String> kafka) { this.kafka = kafka; } // auto-configured
public static void main(String[] args) { SpringApplication.run(RefundsApp.class, args); }
@Bean static RecordFilterStrategy<String, String> cancelledOnly() { // true = discard
return r -> { var h = r.headers().lastHeader("event-type");
return h == null || !"order_cancelled".equals(new String(h.value())); };
}
@KafkaListener(topics = "booknest.order-events", concurrency = "3", filter = "cancelledOnly")
void onCancelled(ConsumerRecord<String, String> event) {
kafka.send("booknest.refund-requests", event.key(), event.value());
}
}14:48:08 ntainer#0-2-C-1 ... partitions assigned: [booknest.order-events-0,
booknest.order-events-1,
booknest.order-events-2]
14:48:18 ntainer#0-1-C-1 ... partitions assigned: [booknest.order-events-1]
14:48:18 ntainer#0-0-C-1 ... partitions assigned: [booknest.order-events-2]concurrency = "3" created three KafkaConsumers on threads #0-0 to #0-2. Under KIP-848 the broker gave all partitions to the first member, then moved one to each of the others ten seconds later without stopping it. Run for 40 seconds (clients/spring_run.sh), it wrote 5,890 requests, one per cancelled order. A listener that throws gets ten deliveries from DefaultErrorHandler, then is logged and skipped; add a DeadLetterPublishingRecoverer to keep failed records (compare Dead Letter Queues).