Support Message argument with payload conversion
The MessageMethodArgumentResolver now also supports applying a MessageConverter to the payload. This is effectively a shortcut for declaring a method with an @Payload argument + MessageHeaders and then creating a new message from the two. Issue: SPR-13288
This commit is contained in:
@@ -158,7 +158,7 @@ public class DefaultMessageHandlerMethodFactory implements MessageHandlerMethodF
|
||||
resolvers.add(new HeadersMethodArgumentResolver());
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new MessageMethodArgumentResolver());
|
||||
resolvers.add(new MessageMethodArgumentResolver(this.messageConverter));
|
||||
|
||||
if (this.customArgumentResolvers != null) {
|
||||
resolvers.addAll(this.customArgumentResolvers);
|
||||
|
||||
@@ -21,8 +21,14 @@ import java.lang.reflect.Type;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.converter.MessageConversionException;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.converter.SmartMessageConverter;
|
||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@code HandlerMethodArgumentResolver} for {@link Message} method arguments.
|
||||
@@ -34,6 +40,19 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class MessageMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final MessageConverter converter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with the given {@link MessageConverter}.
|
||||
* @param converter the MessageConverter to use (required)
|
||||
*/
|
||||
public MessageMethodArgumentResolver(MessageConverter converter) {
|
||||
Assert.notNull(converter, "MessageConverter must not be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return Message.class.isAssignableFrom(parameter.getParameterType());
|
||||
@@ -41,23 +60,32 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
if (!paramType.isAssignableFrom(message.getClass())) {
|
||||
|
||||
Class<?> targetMessageType = parameter.getParameterType();
|
||||
Class<?> targetPayloadType = getPayloadType(parameter);
|
||||
|
||||
if (!targetMessageType.isAssignableFrom(message.getClass())) {
|
||||
String actual = ClassUtils.getQualifiedName(message.getClass());
|
||||
String expected = ClassUtils.getQualifiedName(paramType);
|
||||
String expected = ClassUtils.getQualifiedName(targetMessageType);
|
||||
throw new MethodArgumentTypeMismatchException(message, parameter, "The actual message type " +
|
||||
"[" + actual + "] does not match the expected type [" + expected + "]");
|
||||
}
|
||||
|
||||
Class<?> targetPayloadType = getPayloadType(parameter);
|
||||
Object payload = message.getPayload();
|
||||
if (payload != null && !targetPayloadType.isInstance(payload)) {
|
||||
throw new MethodArgumentTypeMismatchException(message, parameter,
|
||||
"The expected Message<?> payload type [" + ClassUtils.getQualifiedName(targetPayloadType) +
|
||||
"] does not match the actual payload type [" + ClassUtils.getQualifiedName(payload.getClass()) + "]");
|
||||
if (payload == null || targetPayloadType.isInstance(payload)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return message;
|
||||
if (isEmptyPayload(payload)) {
|
||||
String actual = ClassUtils.getQualifiedName(payload.getClass());
|
||||
String expected = ClassUtils.getQualifiedName(targetPayloadType);
|
||||
throw new MessageConversionException(message, "Cannot convert from the " +
|
||||
"expected payload type [" + expected + "] to the " +
|
||||
"actual payload type [" + actual + "] when the payload is empty.");
|
||||
}
|
||||
|
||||
payload = convertPayload(message, parameter, targetPayloadType);
|
||||
return MessageBuilder.createMessage(payload, message.getHeaders());
|
||||
}
|
||||
|
||||
private Class<?> getPayloadType(MethodParameter parameter) {
|
||||
@@ -66,4 +94,42 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
return resolvableType.getGeneric(0).resolve(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given {@code payload} is empty.
|
||||
* @param payload the payload to check (can be {@code null})
|
||||
*/
|
||||
protected boolean isEmptyPayload(Object payload) {
|
||||
if (payload == null) {
|
||||
return true;
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
return ((byte[]) payload).length == 0;
|
||||
}
|
||||
else if (payload instanceof String) {
|
||||
return !StringUtils.hasText((String) payload);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Object convertPayload(Message<?> message, MethodParameter parameter, Class<?> targetPayloadType) {
|
||||
Object result;
|
||||
if (this.converter instanceof SmartMessageConverter) {
|
||||
SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter;
|
||||
result = smartConverter.fromMessage(message, targetPayloadType, parameter);
|
||||
}
|
||||
else {
|
||||
result = this.converter.fromMessage(message, targetPayloadType);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
String actual = ClassUtils.getQualifiedName(targetPayloadType);
|
||||
String expected = ClassUtils.getQualifiedName(message.getPayload().getClass());
|
||||
throw new MessageConversionException(message, "No converter found to convert payload " +
|
||||
"type [" + actual + "] to expected payload type [" + expected + "].");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new PrincipalMethodArgumentResolver());
|
||||
resolvers.add(new MessageMethodArgumentResolver());
|
||||
resolvers.add(new MessageMethodArgumentResolver(this.messageConverter));
|
||||
|
||||
resolvers.addAll(getCustomArgumentResolvers());
|
||||
resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));
|
||||
|
||||
Reference in New Issue
Block a user