In my springboot application i am creating a message that contains a generic type as follows:
public List<QMessage<T>> composeMessage(String entityName, List<T> input) {
if (CollectionUtils.isEmpty(input)) {
return null;
}
log.debug("Composing message to send");
List<QMessage<T>> result = new ArrayList<>();
input.forEach(el -> {
QMessage<T> tmp = new QMessage<>();
tmp.setSourceId(getSourceId(el));
tmp.setEntityName(entityName);
tmp.setData(el);
result.add(tmp);
});
return result;
}
public void convertAndPublishList(List<QMessage<T>> items) {
try {
log.debug("Converting message to JSON and publishing to RabbitMQ");
rabbitTemplate.convertAndSend(MQConfig.WD_COMMONS_QUEUE_EXCHANGE, MQConfig.WD_COMMONS_QUEUE_ROUTING_KEY, items);
} catch (AmqpException e) {
log.error("Error during message publishing to RabbitMQ. Exception: {}", e.getMessage());
throw new BlenderProducerException(e.getMessage(), e);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class QMessage<T> implements Serializable {
private static final long serialVersionUID = 9095736695643670685L;
private Long sourceId;
private String entityName;
private T data;
}
On the listener side I would like to handle the generic somehow, that's what I've tried:
@Slf4j
@Component
@RequiredArgsConstructor
@RabbitListener(queues = MQConfig.WD_COMMONS_QUEUE)
public class WDCommonsListeners {
@RabbitHandler
public void handleA(List<QMessage<Dog>> message) {
System.out.println("handle 1");
}
@RabbitHandler
public void handleB(List<QMessage<Cat>> message) {
System.out.println("handle 2");
}
}
but I got this exception message: org.springframework.amqp.AmqpException: Ambiguous methods for payload type: class java.util.ArrayList: handleA and handleB
Consider that Cat and Dog class are both extending Animal class
I've also tried with an unknown type of message as follow but I receive a LinkedList insted of an object:
@RabbitListener(queues = MQConfig.WD_COMMONS_QUEUE)
public void listener(List<QMessage<?>> message) throws IOException {
if (!CollectionUtils.isEmpty(message)) {
for (QMessage<?> msg : message) {
System.out.println("msg");
}
}
Maybe I have to get JSON from the message and the convert it to object with an ObjectMapper?
CodePudding user response:
It just does not check a generic type of the method parameter: methodParameter.getParameterType().isAssignableFrom(payloadClass)
. So, that's how you got that ambiguity.
Since you logic is hard enough to deal with collection and its deep nested generic type, I'd really suggest to go with a single @RabbitListener
and do respective routing manually.
The DefaultJackson2JavaTypeMapper
has to be configured for the TypePrecedence.TYPE_ID
to make it to convert incoming data to the List<QMessage<?>>
before calling your listener method: https://docs.spring.io/spring-amqp/docs/current/reference/html/#Jackson2JsonMessageConverter-from-message