You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix SmartMessageConverter support in batch listeners
FixesGH-4097
Problem:
When using `@KafkaListener(contentTypeConverter = "...")` with a batch listener
(`batch = "true"`), the SmartMessageConverter was not being applied during
message conversion, resulting in ClassCastException when trying to process
messages that required conversion (e.g., byte[] to String).
Root Cause:
The issue stemmed from two problems:
1. `BatchMessagingMessageConverter` default constructor created an instance
without a `RecordMessageConverter` (`this(null)`), preventing proper
per-record conversion within batch processing.
2. `BatchMessagingMessageListenerAdapter` did not override
`setMessagingConverter()` to propagate the SmartMessageConverter from the
annotation configuration to the batch converter's record converter.
Additionally, the initial approach of bypassing the parent's validation in
`setMessagingConverter()` conflicted with the framework's design to prevent
the "paradox of choice" - users should configure converters either via factory
setter OR annotation attribute, not both.
Solution:
1. Changed `BatchMessagingMessageConverter` default constructor to always
create a `MessagingMessageConverter` by default (`this(new MessagingMessageConverter())`).
This ensures batch converters always have a record converter for per-record
conversion within batches, and enables annotation-only configuration without
requiring users to set converters on the factory.
2. Added `setMessagingConverter()` method to `BatchMessagingMessageConverter`
that propagates the SmartMessageConverter to its internal record converter
when it's an instance of `MessagingMessageConverter`.
3. Overrode `setMessagingConverter()` in `BatchMessagingMessageListenerAdapter`
to:
- Call `super.setMessagingConverter(messageConverter)` first, which applies
the same validation as the parent class (`Assert.isTrue(!this.converterSet)`)
to prevent configuration conflicts between factory setter and annotation.
- Propagate the SmartMessageConverter to the batch converter's record converter
for proper message conversion in batch processing.
This approach:
- Respects the framework's validation to prevent the "paradox of choice"
- Ensures both batch and per-record conversion paths work correctly
- Allows users to configure converters via annotation without needing to set
converters on the factory (annotation-only configuration)
- Avoids the complexity and potential issues of cloning converters
- Works for multiple listeners with different contentTypeConverter values
Testing:
Added comprehensive integration test `BatchSmartMessageConverterTests` that:
- Verifies SmartMessageConverter works with batch listeners using
`@KafkaListener(contentTypeConverter = "...")`
- Tests multiple listeners with different converters to ensure isolation
- Uses `ConcurrentKafkaListenerContainerFactory` with batch mode to verify
annotation attributes propagate correctly to the record converter
Signed-off-by: Jujuwryy <[email protected]>
Copy file name to clipboardExpand all lines: spring-kafka/src/main/java/org/springframework/kafka/listener/adapter/BatchMessagingMessageListenerAdapter.java
+10-8Lines changed: 10 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -114,19 +114,21 @@ public void setBatchToRecordAdapter(BatchToRecordAdapter<K, V> batchToRecordAdap
114
114
* <p>
115
115
* When a {@code SmartMessageConverter} is configured via
116
116
* {@code @KafkaListener(contentTypeConverter = "...")}, this method ensures it is
117
-
* properly propagated to the batch converter, which will then propagate it to the
118
-
* record converter for message conversion in batch listeners.
117
+
* properly propagated to the batch converter's record converter for message conversion
118
+
* in batch listeners.
119
119
* <p>
120
-
* This override does not call the parent implementation because the parent's validation
121
-
* (checking {@code converterSet}) blocks setting the SmartMessageConverter after
122
-
* {@code setBatchMessageConverter} has been called, which is the normal workflow for
123
-
* batch listeners.
120
+
* Uses the same validation as the parent class to prevent the paradox of choice:
121
+
* not allowed when a custom {@link #setBatchMessageConverter(BatchMessageConverter)
122
+
* batchMessageConverter} is provided. Since {@link BatchMessagingMessageConverter} now
123
+
* always has a default {@link org.springframework.kafka.support.converter.MessagingMessageConverter},
124
+
* users can configure the converter via the annotation without needing to set it on the factory.
Copy file name to clipboardExpand all lines: spring-kafka/src/main/java/org/springframework/kafka/support/converter/BatchMessagingMessageConverter.java
+3-2Lines changed: 3 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -89,10 +89,11 @@ public class BatchMessagingMessageConverter implements BatchMessageConverter {
89
89
privatebooleanrawRecordHeader;
90
90
91
91
/**
92
-
* Create an instance that does not convert the record values.
92
+
* Create an instance with a default {@link MessagingMessageConverter} for record conversion.
0 commit comments