Spring Kafka: Producing Messages with KafkaTemplate
KafkaTemplate is the central Spring Kafka class for sending messages. It wraps the Kafka producer and integrates with Spring's exception translation and transaction support.
Contents
Dependency & Configuration
Add spring-kafkaKafkaTemplateapplication.properties
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
# Optional: set client id for easier monitoring
spring.kafka.producer.client-id=my-producer
KafkaTemplate<String, String>spring-kafkaSending a String Message
Inject KafkaTemplatesend()
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
@Service
public class OrderProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public OrderProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
// Send value only — Kafka picks the partition via round-robin or sticky
public void sendOrder(String message) {
kafkaTemplate.send("orders", message);
}
// Send with a key — same key always goes to the same partition
public void sendOrderWithKey(String orderId, String message) {
kafkaTemplate.send("orders", orderId, message);
}
// Send to a specific partition
public void sendToPartition(String message) {
kafkaTemplate.send("orders", 0, null, message); // partition 0
}
}
Sending an Object
To send POJOs, switch the value serializer to JsonSerializerProducerRecord
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.internals.RecordHeader;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
@Service
public class OrderProducer {
private final KafkaTemplate<String, Order> kafkaTemplate;
public OrderProducer(KafkaTemplate<String, Order> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
// Send a POJO — serialized to JSON automatically
public void sendOrder(Order order) {
kafkaTemplate.send("orders", order.getId(), order);
}
// Send with custom headers using ProducerRecord
public void sendWithHeaders(Order order) {
ProducerRecord<String, Order> record = new ProducerRecord<>("orders", order.getId(), order);
record.headers().add(new RecordHeader("source", "order-service".getBytes()));
record.headers().add(new RecordHeader("version", "1".getBytes()));
kafkaTemplate.send(record);
}
}
JsonSerializerJsonDeserializerspring.kafka.consumer.properties.spring.json.trusted.packages=*Send Callbacks & ListenableFuture
kafkaTemplate.send()CompletableFutureSendResultwhenComplete
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class OrderProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public OrderProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendWithCallback(String message) {
CompletableFuture<SendResult<String, String>> future =
kafkaTemplate.send("orders", message);
future.whenComplete((result, ex) -> {
if (ex == null) {
System.out.printf("Sent message [%s] to partition %d offset %d%n",
message,
result.getRecordMetadata().partition(),
result.getRecordMetadata().offset());
} else {
System.err.println("Failed to send message: " + ex.getMessage());
}
});
}
}
future.get()Transactional Sending
Enable Kafka transactions by setting a transaction-id-prefix@Transactional
spring.kafka.producer.transaction-id-prefix=tx-orders-
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
private final KafkaTemplate<String, String> kafkaTemplate;
public OrderService(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
// All sends inside this method are committed atomically
@Transactional
public void processOrder(String orderId, String orderData) {
kafkaTemplate.send("orders", orderId, orderData);
kafkaTemplate.send("audit-log", orderId, "ORDER_CREATED");
// If an exception is thrown here, both sends are rolled back
}
// Programmatic transaction (no @Transactional required)
public void sendProgrammatic(String message) {
kafkaTemplate.executeInTransaction(ops -> {
ops.send("orders", message);
ops.send("audit-log", message);
return true;
});
}
}
Custom ProducerFactory
Override the auto-configured producer by declaring your own ProducerFactoryKafkaTemplateacksdelivery.timeout.ms
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class KafkaProducerConfig {
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
// Reliability: wait for all in-sync replicas to acknowledge
props.put(ProducerConfig.ACKS_CONFIG, "all");
// Retry transient failures for up to 2 minutes (retries stays at its default, unlimited)
props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 120_000);
// Throughput tuning: batch up to 16 KB before sending
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
// Wait up to 5ms to fill the batch
props.put(ProducerConfig.LINGER_MS_CONFIG, 5);
return new DefaultKafkaProducerFactory<>(props);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate(ProducerFactory<String, String> pf) {
return new KafkaTemplate<>(pf);
}
}
enable.idempotence=true