prev: [Operational footprint](05-operational-footprint.md) · [README](../README.md) · next: [The decision table](07-the-decision-table.md) # 6. What Spring adds, and what Boot 4 no longer gives you for free The measurements in this module drive the client libraries directly, so that what is being compared is the brokers rather than three sets of Spring defaults. In an application you would use the Spring integrations, and they are not equivalent to one another. | | Kafka | RabbitMQ | Pulsar | |---|---|---|---| | project | Spring for Apache Kafka 4.1.1 | Spring AMQP 4.1.1 | Spring for Apache Pulsar 2.0.7 | | starter | `spring-boot-starter-kafka` | `spring-boot-starter-amqp` | `spring-boot-starter-pulsar` | | listener | `@KafkaListener` | `@RabbitListener` | `@PulsarListener` | | template | `KafkaTemplate` | `RabbitTemplate` | `PulsarTemplate` | | retry / DLQ | `DefaultErrorHandler`, `@RetryableTopic` | dead-letter exchange, container error handler | `DeadLetterPolicy` on the listener | All three are Boot-managed at 4.1.1, so you do not pin their versions. ## The Boot 4 trap that applies to all three **In Spring Boot 4, depending on a messaging library directly rather than through its Boot starter means you have no auto-configuration.** The auto-configuration classes moved out of `spring-boot-autoconfigure` into per-technology modules that only the starters bring: | depending on | what you lose | how it presents | |---|---|---| | `org.springframework.kafka:spring-kafka` | `spring-boot-kafka` | `No qualifying bean of type KafkaTemplate<...>` — the context starts fine | | `org.springframework.amqp:spring-rabbit` | `spring-boot-amqp` | no `RabbitTemplate`, no `RabbitAdmin` | | `org.springframework.pulsar:spring-pulsar` | `spring-boot-pulsar` | no `PulsarTemplate` | Every Boot 3 tutorial gets this wrong now, and the symptom is a missing bean rather than anything that names the cause. Use the starters. ## The Jackson fork, in both Kafka and RabbitMQ Boot 4 moved to Jackson 3 (`tools.jackson`), and both messaging projects ship converters for both generations. **A `2` in the class name means the previous Jackson** — which is the opposite of the convention you would guess: - Spring Kafka: `JsonSerializer`/`JsonDeserializer` are Jackson 2; `JacksonJsonSerializer`, `JacksonJsonDeserializer` and `JacksonJsonSerde` are Jackson 3. - Spring AMQP: `Jackson2JsonMessageConverter` is Jackson 2; `JacksonJsonMessageConverter` is Jackson 3. Picking the wrong one gives you `SerializationException: Can't serialize data`, whose cause is `Java 8 date/time type java.time.Instant not supported by default` — a message about date types for what is really a wrong-library problem. The [kafka-basics](../kafka-basics/README.md) and [rabbitmq](../rabbitmq/README.md) modules in this repository cover both stacks in detail. next: [The decision table](07-the-decision-table.md)