Introduce SmartMessageConverter interface with conversionHint arguments

Issue: SPR-13343
This commit is contained in:
Juergen Hoeller 2015-08-12 17:45:46 +02:00
parent 5e9a96817b
commit a369fc8afd
5 changed files with 117 additions and 45 deletions

View File

@ -32,8 +32,8 @@ import org.springframework.util.Assert;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
/** /**
* Abstract base class for {@link MessageConverter} implementations including support * Abstract base class for {@link SmartMessageConverter} implementations including
* for common properties and a partial implementation of the conversion methods, * support for common properties and a partial implementation of the conversion methods,
* mainly to check if the converter supports the conversion based on the payload class * mainly to check if the converter supports the conversion based on the payload class
* and MIME type. * and MIME type.
* *
@ -42,7 +42,7 @@ import org.springframework.util.MimeType;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractMessageConverter implements MessageConverter { public abstract class AbstractMessageConverter implements SmartMessageConverter {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
@ -167,19 +167,7 @@ public abstract class AbstractMessageConverter implements MessageConverter {
return fromMessage(message, targetClass, null); return fromMessage(message, targetClass, null);
} }
/** @Override
* A variant of {@link #fromMessage(Message, Class)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a payload parameter into account.
* @param message the input message
* @param targetClass the target class for the conversion
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the result of the conversion, or {@code null} if the converter cannot
* perform the conversion
* @since 4.2
* @see #fromMessage(Message, Class)
*/
public final Object fromMessage(Message<?> message, Class<?> targetClass, Object conversionHint) { public final Object fromMessage(Message<?> message, Class<?> targetClass, Object conversionHint) {
if (!canConvertFrom(message, targetClass)) { if (!canConvertFrom(message, targetClass)) {
return null; return null;
@ -196,19 +184,7 @@ public abstract class AbstractMessageConverter implements MessageConverter {
return toMessage(payload, headers, null); return toMessage(payload, headers, null);
} }
/** @Override
* A variant of {@link #toMessage(Object, MessageHeaders)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a return type into account.
* @param payload the Object to convert
* @param headers optional headers for the message (may be {@code null})
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the new message, or {@code null} if the converter does not support the
* Object type or the target media type
* @since 4.2
* @see #toMessage(Object, MessageHeaders)
*/
public final Message<?> toMessage(Object payload, MessageHeaders headers, Object conversionHint) { public final Message<?> toMessage(Object payload, MessageHeaders headers, Object conversionHint) {
if (!canConvertTo(payload, headers)) { if (!canConvertTo(payload, headers)) {
return null; return null;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,13 +25,17 @@ import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* A {@link MessageConverter} that delegates to a list of other converters * A {@link MessageConverter} that delegates to a list of registered converters
* to be invoked until one of them returns a non-null result. * to be invoked until one of them returns a non-null result.
* *
* <p>As of 4.2.1, this composite converter implements {@link SmartMessageConverter}
* in order to support the delegation of conversion hints.
*
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0 * @since 4.0
*/ */
public class CompositeMessageConverter implements MessageConverter { public class CompositeMessageConverter implements SmartMessageConverter {
private final List<MessageConverter> converters; private final List<MessageConverter> converters;
@ -44,14 +48,10 @@ public class CompositeMessageConverter implements MessageConverter {
this.converters = new ArrayList<MessageConverter>(converters); this.converters = new ArrayList<MessageConverter>(converters);
} }
public List<MessageConverter> getConverters() {
return this.converters;
}
@Override @Override
public Object fromMessage(Message<?> message, Class<?> targetClass) { public Object fromMessage(Message<?> message, Class<?> targetClass) {
for (MessageConverter converter : this.converters) { for (MessageConverter converter : getConverters()) {
Object result = converter.fromMessage(message, targetClass); Object result = converter.fromMessage(message, targetClass);
if (result != null) { if (result != null) {
return result; return result;
@ -60,9 +60,22 @@ public class CompositeMessageConverter implements MessageConverter {
return null; return null;
} }
@Override
public Object fromMessage(Message<?> message, Class<?> targetClass, Object conversionHint) {
for (MessageConverter converter : getConverters()) {
Object result = (converter instanceof SmartMessageConverter ?
((SmartMessageConverter) converter).fromMessage(message, targetClass, conversionHint) :
converter.fromMessage(message, targetClass));
if (result != null) {
return result;
}
}
return null;
}
@Override @Override
public Message<?> toMessage(Object payload, MessageHeaders headers) { public Message<?> toMessage(Object payload, MessageHeaders headers) {
for (MessageConverter converter : this.converters) { for (MessageConverter converter : getConverters()) {
Message<?> result = converter.toMessage(payload, headers); Message<?> result = converter.toMessage(payload, headers);
if (result != null) { if (result != null) {
return result; return result;
@ -71,9 +84,30 @@ public class CompositeMessageConverter implements MessageConverter {
return null; return null;
} }
@Override
public Message<?> toMessage(Object payload, MessageHeaders headers, Object conversionHint) {
for (MessageConverter converter : getConverters()) {
Message<?> result = (converter instanceof SmartMessageConverter ?
((SmartMessageConverter) converter).toMessage(payload, headers, conversionHint) :
converter.toMessage(payload, headers));
if (result != null) {
return result;
}
}
return null;
}
/**
* Return the underlying list of delegate converters.
*/
public List<MessageConverter> getConverters() {
return this.converters;
}
@Override @Override
public String toString() { public String toString() {
return "CompositeMessageConverter[converters=" + this.converters + "]"; return "CompositeMessageConverter[converters=" + getConverters() + "]";
} }
} }

View File

@ -0,0 +1,62 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.converter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
/**
* An extended {@link MessageConverter} SPI with conversion hint support.
*
* <p>In case of a conversion hint being provided, the framework will call
* these extended methods if a converter implements this interface, instead
* of calling the regular {@code fromMessage} / {@code toMessage} variants.
*
* @author Juergen Hoeller
* @since 4.2.1
*/
public interface SmartMessageConverter extends MessageConverter {
/**
* A variant of {@link #fromMessage(Message, Class)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a payload parameter into account.
* @param message the input message
* @param targetClass the target class for the conversion
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the result of the conversion, or {@code null} if the converter cannot
* perform the conversion
* @see #fromMessage(Message, Class)
*/
Object fromMessage(Message<?> message, Class<?> targetClass, Object conversionHint);
/**
* A variant of {@link #toMessage(Object, MessageHeaders)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a return type into account.
* @param payload the Object to convert
* @param headers optional headers for the message (may be {@code null})
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the new message, or {@code null} if the converter does not support the
* Object type or the target media type
* @see #toMessage(Object, MessageHeaders)
*/
Message<?> toMessage(Object payload, MessageHeaders headers, Object conversionHint);
}

View File

@ -24,10 +24,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException; import org.springframework.messaging.MessagingException;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SimpleMessageConverter; import org.springframework.messaging.converter.SimpleMessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -167,8 +167,8 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
} }
MessageConverter converter = getMessageConverter(); MessageConverter converter = getMessageConverter();
Message<?> message = (converter instanceof AbstractMessageConverter ? Message<?> message = (converter instanceof SmartMessageConverter ?
((AbstractMessageConverter) converter).toMessage(payload, messageHeaders, conversionHint) : ((SmartMessageConverter) converter).toMessage(payload, messageHeaders, conversionHint) :
converter.toMessage(payload, messageHeaders)); converter.toMessage(payload, messageHeaders));
if (message == null) { if (message == null) {
String payloadType = (payload != null ? payload.getClass().getName() : null); String payloadType = (payload != null ? payload.getClass().getName() : null);

View File

@ -21,9 +21,9 @@ import java.lang.annotation.Annotation;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -111,8 +111,8 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
return payload; return payload;
} }
else { else {
payload = (this.converter instanceof AbstractMessageConverter ? payload = (this.converter instanceof SmartMessageConverter ?
((AbstractMessageConverter) this.converter).fromMessage(message, targetClass, parameter) : ((SmartMessageConverter) this.converter).fromMessage(message, targetClass, parameter) :
this.converter.fromMessage(message, targetClass)); this.converter.fromMessage(message, targetClass));
if (payload == null) { if (payload == null) {
throw new MessageConversionException(message, throw new MessageConversionException(message,