Skip to content
Kafka Is Fun

Getting Started · Beginner · Topic 4 of 5

Basic @KafkaListener Consumer

@KafkaListener creates a managed listener container that polls Kafka for you. To receive an Acknowledgment and commit when you decide, use a manual acknowledgement mode (MANUAL or MANUAL_IMMEDIATE); in the other modes the Acknowledgment parameter is null.

OrderConsumer.javajava
@Service
@Slf4j
public class OrderConsumer {

    private final OrderService orderService;

    public OrderConsumer(OrderService orderService) {
        this.orderService = orderService;
    }

    @KafkaListener(
        topics = "orders.order.created",
        groupId = "order-processor",
        concurrency = "3",     // three consumers (threads) in this container
        ackMode = "MANUAL"     // per-listener override of the container's ack mode (Spring Kafka 4.1+)
    )
    public void onOrderCreated(ConsumerRecord<String, OrderEvent> record, Acknowledgment ack) {
        log.info("Received order: partition={}, offset={}, key={}",
            record.partition(), record.offset(), record.key());

        orderService.processOrder(record.value());
        ack.acknowledge();   // MANUAL: the offset is committed with the same semantics as BATCH once you acknowledge
        // If processOrder throws, do not acknowledge: the container's error handler decides what happens next
        // (see the DefaultErrorHandler topic).
    }

    // Simple value-only listener (the message converter deserializes the payload)
    @KafkaListener(topics = "orders.order.updated")
    public void onOrderUpdated(OrderEvent event) {
        log.info("Order updated: {}", event.getOrderId());
        orderService.updateOrder(event);
    }
}

Sources

Checked against Spring for Apache Kafka 4.1.1, Spring Boot 4.1.1 and Kafka 4.2 documentation on 11 October 2026. Draft: not yet reviewed by a Kafka specialist, and releases move on, so confirm against the current documentation before you act on it.