Preserve generic SQS listener payload types - #1666
Open
brun0-4ugusto wants to merge 1 commit into
Open
Conversation
Pass listener MethodParameter metadata through the container and message source as a SmartMessageConverter conversion hint so Jackson can resolve complete generic payload types. Add unit, converter, batch, and LocalStack integration coverage for wrappers and nested collections. Fixes awspringgh-1597
brun0-4ugusto
requested review from
MatejNedic,
maciejwalkowiak and
tomazfernandes
as code owners
August 6, 2026 04:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📢 Type of change
📜 Description
This PR fixes generic payload deserialization for
@SqsListenermethods.Previously, payload type inference retained only the raw
Class<?>. Types such asGenericWrapper<TestEvent>andMessage<List<TestEvent>>were therefore reduced toGenericWrapper.classandList.class. Without the complete generic type, Jackson deserialized nested values asLinkedHashMapinstances.This change:
MethodPayloadMetadata, containing the raw payload class and an optional conversion hint.MethodParameteras theSmartMessageConverterconversion hint.MessageConversionContext.SmartMessageConverter#fromMessage(Message, Class, Object)when a conversion hint is available.MessageConvertercontract for converters that do not implementSmartMessageConverter.GenericWrapper<TestEvent>GenericWrapper<List<TestEvent>>Message<List<TestEvent>>List<GenericWrapper<TestEvent>>List<Message<GenericWrapper<TestEvent>>>MethodPayloadTypeInferrerimplementations source-compatible through a default metadata adaptation method.MessageSourceJavadoc to describe where payload conversion occurs.💡 Motivation and Context
@SqsListenerpayload deserialization currently loses generic type information because the inferred listener type is transported only as a rawClass<?>.As a result, Jackson cannot determine the concrete type of generic fields or collection elements and falls back to
LinkedHashMap.The problem is also visible before listener invocation because SQS payload conversion happens at the
MessageSourcelevel. Therefore, interceptors, error handlers, and acknowledgement callbacks may also receive incorrectly typed generic values.Spring's
SmartMessageConverteralready supports a conversion hint. Its base converters can use aMethodParameterhint to recover the complete genericType, so this PR propagates that existing Spring metadata instead of introducing custom Jackson-specific type resolution.Fixes #1597
💚 How did you test it?
Added and executed focused tests covering:
MethodPayloadTypeInferrerimplementations.SmartMessageConverterhint invocation and regularMessageConverterfallback.Validation results:
SqsPayloadTypeInferenceIntegrationTestspassed with LocalStack.Commands used:
./mvnw -pl spring-cloud-aws-sqs -am testFull module: Tests run: 658, Failures: 0, Errors: 0, Skipped: 5📝 Checklist
🔮 Next steps
Subject to maintainer feedback, a separate follow-up PR could expand generic payload inference for listener methods inherited from generic superclasses.
For example:
When Spring discovers the listener method, its original declaration still describes the payload as GenericWrapper. To deserialize it as GenericWrapper, the MethodParameter must also be resolved against the concrete listener class (TestEventListener). This allows Spring's type resolution infrastructure to substitute T with TestEvent before passing the conversion hint to the SmartMessageConverter.
I have already explored and implemented this extension in a separate branch, including tests for inherited generic listener methods and nested or batch generic payload shapes. It is deliberately not included in this PR so that the initial fix remains focused and easier to review.
We can open a discussion about the expected scope and compatibility requirements for inherited generic listeners. If the maintainers agree with the direction, the existing implementation can be refined and submitted as a separate follow-up PR.