“RabbitMQ:Jackson2JsonMessageConverter分析”的版本间差异
跳到导航
跳到搜索
小 (Eijux移动页面Jackson2JsonMessageConverter转换消息的过程分析至RabbitMQ:Jackson2JsonMessageConverter分析,不留重定向) |
(→其他) |
||
(未显示同一用户的27个中间版本) | |||
第2行: | 第2行: | ||
== 关于 == | == 关于 == | ||
在看《Spring 实战》:发送异步消息一节,对于 RabbitMQ(RabbitTemplate)提到基于 JSON 的转换,可以使用 <span style="color: blue">'''Jackson2JsonMessageConverter'''</span> 作为<span style="color: blue">'''消息转换器'''</span>,但是没有提到“在生产者、消费者之间如何实现对象映射”。 | |||
虽然可以想到和 JMS(JmsTemplate)基于 JSON 转换所用到的 <span style="color: blue">MappinJackson2MessageConverter</span> 类似:在生产者、消费者端配置消息转换器时,都设置 typeIdPropertyName、typeIdMappins<ref group="其他" name="MappinJackson2MessageConverter"/>。 | |||
但是对于其流程并不十分清楚,所以通过源代码追踪简单分析如下。 | |||
== 执行过程 == | |||
以下以 RabbitTemplate.convertAndSend(Object object) 为例。 | |||
# '''RabbitTemplate.convertAndSend''': | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public class RabbitTemplate extends ... { | |||
... | |||
// 如果不配置,则默认的 MessageConverter 为 SimpleMessageConverter | |||
private MessageConverter messageConverter = new SimpleMessageConverter(); | |||
... | |||
... | |||
// setter:setMessageConverter | |||
// getter:getMessageConverter | |||
... | |||
... | |||
@Override | |||
public void convertAndSend(Object object) throws AmqpException { | |||
// 使用默认的 exchange、routingKey,而 CorrelationData 为空 | |||
convertAndSend(this.exchange, this.routingKey, object, (CorrelationData) null); | |||
} | |||
@Override | |||
public void convertAndSend(String exchange, String routingKey, final Object object, | |||
@Nullable CorrelationData correlationData) throws AmqpException { | |||
// send 方法发送的信息为 Message 类型,所以需要 convertMessageIfNecessary(object) | |||
send(exchange, routingKey, convertMessageIfNecessary(object), correlationData); | |||
} | |||
@Override | |||
public void send(final String exchange, final String routingKey, | |||
final Message message, @Nullable final CorrelationData correlationData) | |||
throws AmqpException { | |||
execute(channel -> { | |||
doSend(channel, exchange, routingKey, message, | |||
(RabbitTemplate.this.returnsCallback != null | |||
|| (correlationData != null && StringUtils.hasText(correlationData.getId()))) | |||
&& isMandatoryFor(message), | |||
correlationData); | |||
return null; | |||
}, obtainTargetConnectionFactory(this.sendConnectionFactorySelectorExpression, message)); | |||
} | |||
... | |||
// 消息转换:Object -> Message | |||
protected Message convertMessageIfNecessary(final Object object) { | |||
if (object instanceof Message) { | |||
return (Message) object; | |||
} | |||
/** | |||
* 获取“消息转换器”并调用其 toMessage 方法 | |||
* 如果使用【Jackson2JsonMessageConverter】,则实际调用【AbstractMessageConverter.toMessage】 | |||
* | |||
* (Jackson2JsonMessageConverter 只包含构造方法,AbstractMessageConverter 是其父类的父类) | |||
*/ | |||
return getRequiredMessageConverter().toMessage(object, new MessageProperties()); | |||
} | |||
... | |||
private MessageConverter getRequiredMessageConverter() throws IllegalStateException { | |||
MessageConverter converter = getMessageConverter(); // 获取配置的 MessageConverter | |||
if (converter == null) { | |||
throw new AmqpIllegalStateException( | |||
"No 'messageConverter' specified. Check configuration of RabbitTemplate."); | |||
} | |||
return converter; | |||
} | |||
} | |||
</syntaxhighlight> | |||
#* 使用“消息转换器”:'''Jackson2JsonMessageConverter'''<ref group="相关" name="Jackson2JsonMessageConverter"/> 需要配置(见:[[RabbitMQ:Jackson2JsonMessageConverter分析#应用配置]])。 | |||
#* 默认“消息转换器”:'''SimpleMessageConverter'''<ref group="其他" name="SimpleMessageConverter"/>。 | |||
# '''AbstractMessageConverter.toMessage''': | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public abstract class AbstractMessageConverter implements MessageConverter { | |||
... | |||
@Override | |||
public final Message toMessage(Object object, MessageProperties messageProperties) | |||
throws MessageConversionException { | |||
// 转换消息,genericType 为空(但此类的 genericType 并无作用) | |||
return toMessage(object, messageProperties, null); | |||
} | |||
@Override | |||
public final Message toMessage(Object object, @Nullable MessageProperties messagePropertiesArg, | |||
@Nullable Type genericType) | |||
throws MessageConversionException { | |||
MessageProperties messageProperties = messagePropertiesArg; | |||
if (messageProperties == null) { | |||
messageProperties = new MessageProperties(); | |||
} | |||
// 调用 createMessage 方法创建 Message 对象(实际的消息) | |||
Message message = createMessage(object, messageProperties, genericType); | |||
messageProperties = message.getMessageProperties(); | |||
// 保证 Message 的 messageProperties 中 MessageId 不为空 | |||
if (this.createMessageIds && messageProperties.getMessageId() == null) { | |||
messageProperties.setMessageId(UUID.randomUUID().toString()); | |||
} | |||
return message; | |||
} | |||
protected Message createMessage(Object object, MessageProperties messageProperties, @Nullable Type genericType) { | |||
return createMessage(object, messageProperties); // 可以看到 genericType 被抛弃了 | |||
} | |||
/** | |||
* 抽象方法,将会在子类中实现: | |||
* 如果实用【Jackson2JsonMessageConverter】,则实际调用【AbstractJackson2MessageConverter.createMessage】 | |||
*/ | |||
protected abstract Message createMessage(Object object, MessageProperties messageProperties); | |||
} | |||
</syntaxhighlight> | |||
#* '''MessageProperties'''<ref group="相关" name="MessageProperties"/> 用于保存消息的属性信息。 | |||
# '''AbstractJackson2MessageConverter.createMessage''': | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public abstract class AbstractJackson2MessageConverter extends AbstractMessageConverter | |||
implements BeanClassLoaderAware, SmartMessageConverter { | |||
... | |||
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; | |||
protected final ObjectMapper objectMapper; | |||
private MimeType supportedContentType; | |||
private String supportedCTCharset; | |||
@Nullable | |||
private ClassMapper classMapper = null; // !!! | |||
private Charset defaultCharset = DEFAULT_CHARSET; | |||
... | |||
private Jackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); // !!! | |||
... | |||
private boolean charsetIsUtf8 = true; | |||
... | |||
// 构造方法(以下以 Jackson2JsonMessageConverter 的无参构造为例) | |||
protected AbstractJackson2MessageConverter(ObjectMapper objectMapper, MimeType contentType, | |||
String... trustedPackages) { | |||
Assert.notNull(objectMapper, "'objectMapper' must not be null"); | |||
Assert.notNull(contentType, "'contentType' must not be null"); | |||
this.objectMapper = objectMapper; // 为 new ObjectMapper()的对象 | |||
this.supportedContentType = contentType; // 为 "application/json" 的 MimeType(MIME 类型) | |||
this.supportedCTCharset = this.supportedContentType.getParameter("charset"); | |||
((DefaultJackson2JavaTypeMapper) this.javaTypeMapper).setTrustedPackages(trustedPackages); | |||
} | |||
... | |||
@Override | |||
protected Message createMessage(Object objectToConvert, MessageProperties messageProperties) | |||
throws MessageConversionException { | |||
// 创建 Message 对象,genericType 为空 | |||
return createMessage(objectToConvert, messageProperties, null); | |||
} | |||
@Override | |||
protected Message createMessage(Object objectToConvert, MessageProperties messageProperties, | |||
@Nullable Type genericType) throws MessageConversionException { | |||
byte[] bytes; | |||
// 通过 objectMapper 将 objectToConvert(要发送的对象)转换为 byte[] | |||
try { | |||
// 默认:以 Utf8(JsonEncoding.UTF8) 转换对象 | |||
if (this.charsetIsUtf8 && this.supportedCTCharset == null) { | |||
bytes = this.objectMapper.writeValueAsBytes(objectToConvert); | |||
} | |||
// 以设置的 supportedCTCharset 转换对象 | |||
else { | |||
String jsonString = this.objectMapper | |||
.writeValueAsString(objectToConvert); | |||
String encoding = this.supportedCTCharset != null ? this.supportedCTCharset : getDefaultCharset(); | |||
bytes = jsonString.getBytes(encoding); | |||
} | |||
} | |||
catch (IOException e) { | |||
throw new MessageConversionException("Failed to convert Message content", e); | |||
} | |||
/** | |||
* messageProperties:设置内容的 MIME 类型 | |||
* | |||
* supportedContentType由“构造方法、set方法”指定,默认:"application/json" | |||
*/ | |||
messageProperties.setContentType(this.supportedContentType.toString()); | |||
/** | |||
* messageProperties:设置内容的字符集 | |||
* | |||
* supportedCTCharset由“构造方法、set方法”指定,默认:StandardCharsets.UTF_8 | |||
*/ | |||
if (this.supportedCTCharset == null) { | |||
messageProperties.setContentEncoding(getDefaultCharset()); | |||
} | |||
// messageProperties:设置内容的长度 | |||
messageProperties.setContentLength(bytes.length); | |||
/** | |||
* messageProperties:【设置对象类型!!!】 | |||
* | |||
* 1、classMapper:只能由“set方法”指定,默认:空 | |||
* | |||
* 2、javaTypeMapper:只能由“set方法”指定,默认已设置为:DefaultJackson2JavaTypeMapper | |||
*/ | |||
if (getClassMapper() == null) { | |||
//【通过 objectMapper 构建 JavaType 对象】 | |||
JavaType type = this.objectMapper.constructType( | |||
genericType == null ? objectToConvert.getClass() : genericType); | |||
if (genericType != null && !type.isContainerType() | |||
&& Modifier.isAbstract(type.getRawClass().getModifiers())) { | |||
type = this.objectMapper.constructType(objectToConvert.getClass()); | |||
} | |||
/** | |||
* 通过 javaTypeMapper 的配置(Jackson2JavaTypeMapper 的实现类), | |||
* 将 JavaType 设置到 messageProperties | |||
* | |||
* 【默认将调用 DefaultJackson2JavaTypeMapper.fromJavaType】 | |||
*/ | |||
getJavaTypeMapper().fromJavaType(type, messageProperties); | |||
} | |||
else { | |||
/** | |||
* 通过 classMapper 的配置(ClassMapper 的实现类), | |||
* 将 objectToConvert.getClass() 设置到 messageProperties | |||
*/ | |||
getClassMapper().fromClass(objectToConvert.getClass(), messageProperties); // NOSONAR never null | |||
} | |||
return new Message(bytes, messageProperties); | |||
} | |||
} | |||
</syntaxhighlight> | |||
#* AbstractJackson2MessageConverter 包含了“ObjectMapper”和“ClassMapper”类型的属性。 | |||
#*# '''ObjectMapper'''<ref group="相关" name="ObjectMapper"/>:用于生成“'''JavaType'''<ref group="相关" name="JavaType"/> 的对象”(用于表示“要发送对象的类型”)。 | |||
#*# '''ClassMapper'''<ref group="相关" name="ClassMapper"/>:用于向 '''MessageProperties''' <ref group="相关" name="MessageProperties"/> 添加“要发送对象的类型”。 | |||
# '''DefaultJackson2JavaTypeMapper<ref group="相关" name="DefaultJackson2JavaTypeMapper"/>.fromJavaType''': | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper { | |||
... | |||
/** | |||
* 以下 addHeader、getClassIdFieldName、getContentClassIdFieldName、getKeyClassIdFieldName 方法 | |||
* 均已在父类 AbstractJavaTypeMapper 中实现 | |||
*/ | |||
@Override | |||
public void fromJavaType(JavaType javaType, MessageProperties properties) { | |||
/** | |||
* 向 messageProperties 的【“__TypeId__”】中写入 javaType 的“原始类型”【即,对象本身的类型】 | |||
*/ | |||
addHeader(properties, getClassIdFieldName(), javaType.getRawClass()); | |||
/** | |||
* 如果 javaType 表示的类型是“数组类型”以外的“容器类型”,【即,对象是一个容器类型】 | |||
* 向 messageProperties 的【“__ContentTypeId__”】中写入 javaType 元素的类型【即,对象的元素的类型】 | |||
* | |||
* P.S. “容器类型”:包括数组、映射和集合类型 | |||
*/ | |||
if (javaType.isContainerType() && !javaType.isArrayType()) { | |||
addHeader(properties, getContentClassIdFieldName(), javaType.getContentType().getRawClass()); | |||
} | |||
/** | |||
* 如果 javaType 的 _keyType 不为空,【即,对象是一个映射类型】 | |||
* 向 messageProperties 的【“__KeyTypeId__”】中写入 javaType 的 _keyType 的类型【即,对象的 key 的类型】 | |||
* | |||
* P.S. _keyType 用于表示 MapLikeType(表示一个 Map-like 的类型)的“键的类型” | |||
*/ | |||
if (javaType.getKeyType() != null) { | |||
addHeader(properties, getKeyClassIdFieldName(), javaType.getKeyType().getRawClass()); | |||
} | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
# '''AbstractJavaTypeMapper.addHeader''': | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public abstract class AbstractJavaTypeMapper implements BeanClassLoaderAware { | |||
... | |||
// 用于保存 JavaType 的“原始类型” | |||
public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__"; | |||
// 用于保存 JavaType 的“元素的类型” | |||
public static final String DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__ContentTypeId__"; | |||
// 用于保存 JavaType 的“key 的类型” | |||
public static final String DEFAULT_KEY_CLASSID_FIELD_NAME = "__KeyTypeId__"; | |||
// 用于保存 String -> Class<?> 的映射 | |||
private final Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>(); | |||
// 用于保存 Class<?> -> String 的映射:由 idClassMapping 的键值反转得到 | |||
private final Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>(); | |||
... | |||
public String getClassIdFieldName() { | |||
return DEFAULT_CLASSID_FIELD_NAME; | |||
} | |||
public String getContentClassIdFieldName() { | |||
return DEFAULT_CONTENT_CLASSID_FIELD_NAME; | |||
} | |||
public String getKeyClassIdFieldName() { | |||
return DEFAULT_KEY_CLASSID_FIELD_NAME; | |||
} | |||
// 设置 idClassMapping: | |||
public void setIdClassMapping(Map<String, Class<?>> idClassMapping) { | |||
this.idClassMapping.putAll(idClassMapping); | |||
createReverseMap(); | |||
} | |||
// 设置 classIdMapping:将 idClassMapping 的键值反转 | |||
private void createReverseMap() { | |||
this.classIdMapping.clear(); | |||
for (Map.Entry<String, Class<?>> entry : this.idClassMapping.entrySet()) { | |||
String id = entry.getKey(); | |||
Class<?> clazz = entry.getValue(); | |||
this.classIdMapping.put(clazz, id); | |||
} | |||
} | |||
... | |||
protected void addHeader(MessageProperties properties, String headerName, Class<?> clazz) { | |||
/** | |||
* 向 MessageProperties.headers 中添加(“要发送对象的”)类型: | |||
* | |||
* 1、如果 classIdMapping 中存在 String -> Class<?> 的映射,则写入对应的 String | |||
* | |||
* 2、否则,写入 Class<?> 的名称 | |||
*/ | |||
if (this.classIdMapping.containsKey(clazz)) { | |||
properties.getHeaders().put(headerName, this.classIdMapping.get(clazz)); | |||
} | |||
else { | |||
properties.getHeaders().put(headerName, clazz.getName()); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
=== 相关 === | |||
<references group="相关"> | |||
【相关:Jackson2JsonMessageConverter】 | |||
<ref group="相关" name="Jackson2JsonMessageConverter"> | |||
'''<span style="color: blue">Jackson2JsonMessageConverter</span> 的类层次:''' | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
Jackson2JsonMessageConverter extends AbstractJackson2MessageConverter | |||
AbstractJackson2MessageConverter extends AbstractMessageConverter implements BeanClassLoaderAware, SmartMessageConverter | |||
AbstractMessageConverter implements MessageConverter | |||
</syntaxhighlight> | |||
# '''Jackson2JsonMessageConverter''':【类】只有构造方法,其他实用方法都在父类实现。 | |||
#* 构造方法:用于指定 objectMapper、supportedContentType 等 | |||
# '''AbstractJackson2MessageConverter''':【抽象类】但包括了几乎所有属性和方法。 —— 【核心】 | |||
#* 属性: | |||
#*: <span style="color: blue">ClassMapper</span> / <span style="color: blue">Jackson2JavaTypeMapper</span>:用于向 <span style="color: blue">MessageProperties.headers</span> 中写入“<span style="color: green">__TypeId__</span>”等字段; | |||
#* 方法:(都已实现) | |||
#*: <span style="color: blue">createMessage</span>:用于“Object -> Message”转换 | |||
#*: <span style="color: blue">fromMessage</span>:用于“Message -> Object”转换 | |||
# '''AbstractMessageConverter''':【抽象类】只包括了部分方法(“Object -> Message”),其余在子类实现。 | |||
#* 方法: | |||
#*: toMessage:用于“Object -> Message”转换,将调用 createMessage 方法 | |||
#*: createMessage:(调用子类的 createMessage 方法) | |||
#* <span style="color: blue">SimpleMessageConverter</span>也间接继承了该抽象类。 ——【RabbitTemplate 默认的 MessageConverter】 | |||
# '''MessageConverter''':【接口】消息转换器接口。 | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public interface MessageConverter { | |||
// Convert a Java object to a Message. | |||
Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException; | |||
default Message toMessage(Object object, MessageProperties messageProperties, @Nullable Type genericType) | |||
throws MessageConversionException { | |||
return toMessage(object, messageProperties); | |||
} | |||
// Convert from a Message to a Java object. | |||
Object fromMessage(Message message) throws MessageConversionException; | |||
} | |||
</syntaxhighlight> | |||
'''<span style="color: blue">Jackson2JsonMessageConverter</span> 类:'''只有构造方法(用于指定 ObjectMapper 等) | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
public class Jackson2JsonMessageConverter extends AbstractJackson2MessageConverter { | |||
// 1、无参构造 | |||
public Jackson2JsonMessageConverter() { | |||
// 将调用 2、含参(trustedPackages)构造,并指定了 trustedPackages 为 "*" | |||
this("*"); | |||
} | |||
// 2、含参(trustedPackages)构造 | |||
public Jackson2JsonMessageConverter(String... trustedPackages) { | |||
// 将调用 4、含参(trustedPackages、trustedPackages)构造,并指定了 ObjectMapper 为 ObjectMapper 对象 | |||
this(new ObjectMapper(), trustedPackages); | |||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |||
} | |||
// 3、含参(ObjectMapper)构造 | |||
public Jackson2JsonMessageConverter(ObjectMapper jsonObjectMapper) { | |||
// 将调用 4、含参(trustedPackages、trustedPackages)构造,并指定了 trustedPackages 为 "*" | |||
this(jsonObjectMapper, "*"); | |||
} | |||
// 4、含参(trustedPackages、trustedPackages)构造 | |||
public Jackson2JsonMessageConverter(ObjectMapper jsonObjectMapper, String... trustedPackages) { | |||
// 将调用父类(AbstractJackson2MessageConverter)的构造方法 | |||
// 并指定了父类的 supportedContentType 属性 ————(MessageProperties.CONTENT_TYPE_JSON 为 【"application/json"】) | |||
super(jsonObjectMapper, MimeTypeUtils.parseMimeType(MessageProperties.CONTENT_TYPE_JSON), trustedPackages); | |||
} | |||
} | |||
</syntaxhighlight> | |||
: 如上,使用无参构造时: | |||
:* objectMapper 为 <span style="color: blue">ObjectMapper</span> 实例 | |||
:* MimeType 为 <span style="color: green">"application/json"</span> | |||
</ref> | |||
【相关:MessageProperties】 | |||
<ref group="相关" name="MessageProperties"> | |||
'''<span style="color: blue">MessageProperties</span>''':用于保存消息的各项属性 | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
public class MessageProperties implements Serializable { | |||
... | |||
public static final String CONTENT_TYPE_JSON = "application/json"; | |||
... | |||
private final Map<String, Object> headers = new HashMap<>(); // 以 Map 保存的头信息 | |||
... | |||
private String messageId; | |||
... | |||
public Map<String, Object> getHeaders() { | |||
return this.headers; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
* MessageProperties 是 Message 类的属性(二者都位于:“'''org.springframework.amqp.core'''”)。 | |||
</ref> | |||
【相关:ObjectMapper】 | |||
<ref group="相关" name="ObjectMapper"> | |||
'''<span style="color: blue">ObjectMapper</span>:'''用于快速的进行各个类型和Json类型的相互转换。 | |||
<blockquote> | |||
ObjectMapper 提供了'''用于读取和写入 JSON''' 的功能,无论是从基本的 POJO(普通的旧 Java 对象)还是从通用的 JSON 树模型({@link JsonNode})读取和写入JSON,以及用于执行转换的相关功能。 | |||
</blockquote> | |||
* 位于:“'''com.fasterxml.jackson.databind'''” | |||
* 参考:<big>'''【[https://blog.csdn.net/technologyleader/article/details/123358279 Java ObjectMapper详解]】'''</big> | |||
</ref> | |||
【相关:ClassMapper】 | |||
<ref group="相关" name="ClassMapper"> | |||
'''<span style="color: blue">ClassMapper</span>:'''(接口)'''用于在消息上设置元数据(MessageProperties)''',以便可以在接收消息时创建需要实例化的类。 | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
public interface ClassMapper { | |||
void fromClass(Class<?> clazz, MessageProperties properties); | |||
Class<?> toClass(MessageProperties properties); | |||
} | |||
</syntaxhighlight> | |||
* 子接口: | |||
*: <syntaxhighlight lang="Java" highlight=""> | |||
public interface Jackson2JavaTypeMapper extends ClassMapper { | |||
</syntaxhighlight> | |||
* 实现类: | |||
*: <syntaxhighlight lang="Java" highlight=""> | |||
public class DefaultClassMapper implements ClassMapper, InitializingBean { | |||
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper { | |||
</syntaxhighlight> | |||
</ref> | |||
【相关:DefaultJackson2JavaTypeMapper】 | |||
<ref group="相关" name="DefaultJackson2JavaTypeMapper"> | |||
'''<span style="color: blue">DefaultJackson2JavaTypeMapper</span>''': | |||
: AbstractJackson2MessageConverter('''Jackson2JsonMessageConverter''') 默认的 '''Jackson2JavaTypeMapper'''(ClassMapper) | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper { | |||
... | |||
@Override | |||
public void fromJavaType(JavaType javaType, MessageProperties properties) { | |||
... | |||
} | |||
@Override | |||
public JavaType toJavaType(MessageProperties properties) { | |||
... | |||
} | |||
@Override | |||
public void fromClass(Class<?> clazz, MessageProperties properties) { | |||
fromJavaType(TypeFactory.defaultInstance().constructType(clazz), properties); | |||
} | |||
@Override | |||
public Class<?> toClass(MessageProperties properties) { | |||
return toJavaType(properties).getRawClass(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
</ref> | |||
【相关:JavaType】 | |||
<ref group="相关" name="JavaType"> | |||
'''<span style="color: blue">JavaType</span>''':'''类型标记类的基类''',用于包含信息和用作反序列化程序的键。 | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
package com.fasterxml.jackson.databind; | |||
import ... | |||
public abstract class JavaType | |||
extends ResolvedType | |||
implements java.io.Serializable, // 2.1 | |||
java.lang.reflect.Type // 2.2 | |||
{ | |||
</syntaxhighlight> | |||
:* 父类:<span style="color: blue">'''java.lang.reflect.Type'''</span> ——(参考:<big>'''【[[Type接口:Java中的类型]]】'''</big>) | |||
:* 子类:(“TypeBase”:<syntaxhighlight lang="Java" inline>TypeBase extends JavaType implements JsonSerializable</syntaxhighlight>) | |||
::{| class="wikitable" | |||
! Class !! Parent !! Description | |||
|- | |||
| <span style="color: blue">'''SimpleType'''</span> || TypeBase | |||
| 简单类型:定义为<span style="color: blue">'''除已识别的容器类型(数组、集合、映射)之外的任何类型'''</span> | |||
* '''自定义的类,对应的都是 SimpleType''' | |||
|- | |||
| <span style="color: blue">'''ArrayType'''</span> || TypeBase | |||
| 数组类型表示 <span style="color: blue">'''Java 数组'''</span>。 ——'''包括“基本数组”和“对象值数组”''' | |||
* 此外,对象值数组可以具有任何其他元素类型。 | |||
|- | |||
| <span style="color: blue">'''CollectionType'''</span> || CollectionLikeType | |||
| 表示 <span style="color: blue">'''Java 集合类型(列表、集)'''</span>的类型。 | |||
|- | |||
| '''CollectionLikeType''' || TypeBase | |||
| 表示'''类似于集合类型'''的事物的类型。 ——不一定实现“{@link java.util.Collection}” | |||
* 这特别允许框架检查用于 Map 类型的配置和注释设置,并将其传递给可能更熟悉实际类型的自定义处理程序。 | |||
|- | |||
| <span style="color: blue">'''MapType'''</span> || MapLikeType | |||
| 表示“真正的”<span style="color: blue">'''Java 映射类型'''</span>的类型。 | |||
|- | |||
| '''MapLikeType''' || TypeBase | |||
| 表示'''类似映射类型'''的事物的类型,由键值对组成。 ——不一定实现“{@link java.util.Map}” | |||
* 但没有足够的内省功能来允许某种级别的泛型处理。 | |||
* 这特别允许框架检查用于 Map 类型的配置和注释设置,并将其传递给可能更熟悉实际类型的自定义处理程序。 | |||
|- | |||
| PlaceholderForType || TypeBase | |||
| 侦测已解析类型的绑定时使用的帮助程序类型,这是专用化所必需的。 | |||
|- | |||
| ResolvedRecursiveType || TypeBase | |||
| 用于自引用的内部占位符类型。 | |||
|- | |||
| ReferenceType || SimpleType | |||
| 专用 {@link SimpleType},用于引用类型类型,即可以取消引用到不同类型的另一个值(或 null)的值。 | |||
* 引用的类型可以使用 {@link getContentType()} 访问。 | |||
|} | |||
</ref> | |||
</references> | |||
== 应用配置<ref group="参考">SpringBoot 集成 RabbitMQ 参考: | |||
# [https://blog.csdn.net/future_spring/article/details/106653785 优雅的解决SpringBoot集成RabbitMQ序列化和反序列化的思路1] | |||
# [https://blog.csdn.net/future_spring/article/details/106690434?spm=1001.2014.3001.5502 优雅的解决SpringBoot集成RabbitMQ序列化和反序列化的思路2] | |||
# [https://blog.csdn.net/future_spring/article/details/106752494?spm=1001.2014.3001.5502 优雅的解决SpringBoot集成RabbitMQ序列化和反序列化的思路3 与Reflections简单介绍] | |||
</ref> == | |||
由以上过程可知(使用 Jackson2JsonMessageConverter 时): | |||
1、Jackson2JsonMessageConverter 的构造方法用于指定 objectMapper、supportedContentType(消息的内容类型) | |||
2、Jackson2JsonMessageConverter 的 Jackson2JavaTypeMapper(用于 JavaType 映射)默认为 DefaultJackson2JavaTypeMapper | |||
DefaultJackson2JavaTypeMapper 继承与 AbstractJavaTypeMapper(抽象类) | |||
AbstractJavaTypeMapper 中: | |||
1、__TypeId__:用于指定 MessageProperties.headers 中保存“要发送对象的类型”的字段名称 | |||
2、idClassMapping:用于保存“Class -> String”的映射 | |||
MessageProperties.headers(Map<String, Object> 类型)的写入过程: | |||
1、headers 的 key:写入“__TypeId__”; | |||
2、headers 的 value:如果 AbstractJavaTypeMapper.idClassMapping 中有对应的映射,则写入对应 '''String''',否则,写入“要发送对象的类型”的'''全限定名'''。 | |||
在消息的“生产者”和“消费者”之间传递消息,则必须: | |||
# 依赖相同的 jar; | |||
# 拥有相同(全限定名相同)的类; | |||
# 获取 Message,然后自己解析; | |||
对于“生产者”和“消费者”之间的解耦,必须: | |||
1、配置 Jackson2JsonMessageConverter:设置 '''DefaultJackson2JavaTypeMapper''' 的 '''idClassMapping'''。 | |||
2、配置 Jackson2JsonMessageConverter:使用自定义的 '''ClassMapper'''(fromClass、toClass)和 '''JavaTypeMapper'''(fromJavaType、toJavaType)。 | |||
Jackson2JsonMessageConverter 配置: | |||
# 方案一:<span style="color: blue">设置 '''DefaultJackson2JavaTypeMapper''' 的 '''idClassMapping'''</span> | |||
## 生产者: | |||
##: <syntaxhighlight lang="Java" highlight=""> | |||
@Configuration | |||
public class RabbitConfiguration { | |||
@Bean | |||
public MessageConverter messageConverter() { | |||
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); | |||
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); | |||
// Producer: String -> Class<?> | |||
Map<String, Class<?>> idClassMapping = new HashMap<>(); | |||
idClassMapping.put("Bean1", producer.Bean1.class); | |||
idClassMapping.put("Bean2", producer.Bean2.class); | |||
// ... more setting | |||
javaTypeMapper.setIdClassMapping(idClassMapping); | |||
messageConverter.setJavaTypeMapper(javaTypeMapper); | |||
return messageConverter; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
## 消费者: | |||
##: <syntaxhighlight lang="Java" highlight=""> | |||
@Configuration | |||
public class RabbitConfiguration { | |||
@Bean | |||
public MessageConverter messageConverter() { | |||
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); | |||
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); | |||
// Consumer: String -> Class<?> | |||
Map<String, Class<?>> idClassMapping = new HashMap<>(); | |||
idClassMapping.put("Bean1", consumer.Bean1.class); | |||
idClassMapping.put("Bean2", consumer.Bean2.class); | |||
// ... more setting | |||
javaTypeMapper.setIdClassMapping(idClassMapping); | |||
messageConverter.setJavaTypeMapper(javaTypeMapper); | |||
return messageConverter; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
# 方案二:<span style="color: blue">使用自定义的 '''ClassMapper'''和 '''JavaTypeMapper'''</span> | |||
## 生产者: | |||
##: <syntaxhighlight lang="Java" highlight=""> | |||
@Configuration | |||
public class RabbitConfiguration { | |||
@Bean | |||
public MessageConverter messageConverter() { | |||
// Producer: Class<?> -> String | |||
Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>(); | |||
classIdMapping.put(producer.Bean1.class, "Bean1"); | |||
classIdMapping.put(producer.Bean2.class, "Bean2"); | |||
// ... more setting | |||
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); | |||
messageConverter.setClassMapper(new ClassMapper() { | |||
... | |||
@Override | |||
public void fromClass(Class<?> clazz, MessageProperties properties) { | |||
// Set "__TypeId__" with classIdMapping | |||
properties.setHeader("__TypeId__", classIdMapping.get(clazz)); | |||
} | |||
}); | |||
return messageConverter; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
## 消费者: | |||
##: <syntaxhighlight lang="Java" highlight=""> | |||
@Configuration | |||
public class RabbitConfiguration { | |||
@Bean | |||
public MessageConverter messageConverter() { | |||
// Consumer: String -> Class<?> | |||
Map<String, Class<?>> idClassMapping = new HashMap<>(); | |||
idClassMapping.put("Bean1", consumer.Bean1.class); | |||
idClassMapping.put("Bean2", consumer.Bean2.class); | |||
// ... more setting | |||
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); | |||
messageConverter.setClassMapper(new ClassMapper() { | |||
... | |||
@Override | |||
public Class<?> toClass(MessageProperties properties) { | |||
// Get "__TypeId__" | |||
String typeId = (String) properties.getHeaders().get("__TypeId__"); | |||
// Get Class<?> with classIdMapping | |||
return idClassMapping.get(typeId); | |||
} | |||
}); | |||
return messageConverter; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
== 参考&其他 == | |||
=== 参考 === | |||
<references group="参考"/> | |||
=== 其他 === | |||
<references group="其他"> | |||
【相关:SimpleMessageConverter】 | |||
<ref group="其他" name="MappinJackson2MessageConverter"> | |||
'''<span style="color: blue">JMS</span> 的 <span style="color: blue">MappinJackson2MessageConverter</span> 配置''':【分别在“生产者”、“消费者”中配置】 | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
@Configuration | |||
public class JmsConfiguration { | |||
@Bean | |||
public MappingJackson2MessageConverter messageConverter(){ | |||
MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter(); | |||
// 1、设置: typeIdPropertyName | |||
messageConverter.setTypeIdPropertyName("_typeId"); | |||
// 添加 Map<String, Class<?>>:用于保存“String -> Class<?>”的映射 | |||
Map<String, Class<?>> typeIdMapping = new HashMap<>(); | |||
typeIdMapping.put("Bean1", bean1.class); | |||
typeIdMapping.put("Bean2", bean2.class); | |||
// 2、设置:typeIdMapping | |||
messageConverter.setTypeIdMappings(typeIdMapping); | |||
return messageConverter; | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
* 在“生产者”、“消费者”间传递消息时,会通过 <span style="color: blue">Message.setStringProperty(String name, String value)</span> 方法设置对象的类型,其中: | |||
*# name:为 “typeIdPropertyName 属性”的值, | |||
*# value:为(通过“classIdMappings 属性”映射的)表示对象类型的 String 值。 | |||
* '''MappingJackson2MessageConverter''' 类: | |||
** 属性: | |||
**# <span style="color: blue">typeIdPropertyName</span>:保存“Message 中表示对象类型的字段的名称”; —— <span style="color: red">'''没有默认值,必须设置'''</span> | |||
**# <span style="color: blue">idClassMappings</span>:保存“'''String -> Class<?>'''”的映射; | |||
**# <span style="color: blue">classIdMappings</span>:保存“'''Class<?> -> String'''”的映射; | |||
** 方法: | |||
**# <span style="color: blue">setTypeIdPropertyName(...)</span>:用于设置 typeIdPropertyName 属性; | |||
**# <span style="color: blue">setTypeIdMappings(...)</span>:用于设置 idClassMappings 和 classIdMappings 属性; | |||
**# <span style="color: blue">toMessage(...)</span>:用于“Object -> Message”转换,需要 classIdMappings 属性; | |||
**# <span style="color: blue">fromMessage(...)</span>:用于“Message -> Object”转换,需要 idClassMappings 属性; | |||
** 该类位于“'''org.springframework.jms.support.converter'''”包(区别于“org.springframework.messaging.converter”包下的同名类)。 | |||
</ref> | |||
【相关:SimpleMessageConverter】 | |||
<ref group="其他" name="SimpleMessageConverter"> | |||
'''<span style="color: blue">SimpleMessageConverter</span>:'''【RabbitTemplate 默认的 MessageConverter】 | |||
: <syntaxhighlight lang="Java" highlight=""> | |||
public class SimpleMessageConverter extends AllowedListDeserializingMessageConverter implements BeanClassLoaderAware { | |||
public static final String DEFAULT_CHARSET = "UTF-8"; | |||
private volatile String defaultCharset = DEFAULT_CHARSET; | |||
... | |||
public void setDefaultCharset(String defaultCharset) { | |||
this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET; | |||
} | |||
... | |||
/** | |||
* 由给定的 Object 创建 AMQP Message。 | |||
* | |||
* 如下,支持的载荷为:byte[]、String、Serializable | |||
*/ | |||
@Override | |||
protected Message createMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { | |||
byte[] bytes = null; | |||
/** | |||
* 转换消息载荷(仅支持:byte[]、String、Serializable) | |||
*/ | |||
if (object instanceof byte[]) { | |||
bytes = (byte[]) object; | |||
// messageProperties:设置内容的 MIME 类型为 "application/octet-stream" | |||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_BYTES); | |||
} | |||
else if (object instanceof String) { | |||
try { | |||
bytes = ((String) object).getBytes(this.defaultCharset); | |||
} | |||
catch (UnsupportedEncodingException e) { | |||
throw new MessageConversionException( | |||
"failed to convert to Message content", e); | |||
} | |||
// messageProperties:设置内容的 MIME 类型为 "text/plain" | |||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN); | |||
/// messageProperties:设置内容的字符集(由“set方法”指定,默认为:"UTF-8") | |||
messageProperties.setContentEncoding(this.defaultCharset); | |||
} | |||
else if (object instanceof Serializable) { | |||
try { | |||
bytes = SerializationUtils.serialize(object); | |||
} | |||
catch (IllegalArgumentException e) { | |||
throw new MessageConversionException( | |||
"failed to convert to serialized Message content", e); | |||
} | |||
// messageProperties:设置内容的 MIME 类型为 "application/x-java-serialized-object" | |||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT); | |||
} | |||
/** | |||
* 生成 AMQP Message 并返回 | |||
*/ | |||
if (bytes != null) { | |||
// messageProperties:设置内容的长度 | |||
messageProperties.setContentLength(bytes.length); | |||
return new Message(bytes, messageProperties); | |||
} | |||
throw new IllegalArgumentException(getClass().getSimpleName() | |||
+ " only supports String, byte[] and Serializable payloads, received: " + object.getClass().getName()); | |||
} | |||
@Override | |||
public Object fromMessage(Message message) throws MessageConversionException { | |||
... | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
“'''SimpleMessageConverter'''”与“'''Jackson2JsonMessageConverter'''”(AbstractJackson2MessageConverter)区别: | |||
* 相同:都(间接)继承于“AbstractMessageConverter”。 | |||
* 不同: | |||
*# '''不能在“生产者”、“消费者”间传递对象的类型'''。 | |||
*#: ——因为没有“<span style="color: blue">ClassMapper</span>”/“<span style="color: blue">Jackson2JavaTypeMapper</span>”属性,所以不会向 <span style="color: blue">MessageProperties.headers</span> 中写入“<span style="color: blue">__TypeId__</span>”等字段。 | |||
*# '''只支持:<span style="color: green">byte[]、String、Serializable</span> 类型的消息载荷'''。 | |||
</ref> | |||
</references> |
2022年12月28日 (三) 22:11的最新版本
关于
在看《Spring 实战》:发送异步消息一节,对于 RabbitMQ(RabbitTemplate)提到基于 JSON 的转换,可以使用 Jackson2JsonMessageConverter 作为消息转换器,但是没有提到“在生产者、消费者之间如何实现对象映射”。 虽然可以想到和 JMS(JmsTemplate)基于 JSON 转换所用到的 MappinJackson2MessageConverter 类似:在生产者、消费者端配置消息转换器时,都设置 typeIdPropertyName、typeIdMappins[其他 1]。 但是对于其流程并不十分清楚,所以通过源代码追踪简单分析如下。
执行过程
以下以 RabbitTemplate.convertAndSend(Object object) 为例。
- RabbitTemplate.convertAndSend:
public class RabbitTemplate extends ... { ... // 如果不配置,则默认的 MessageConverter 为 SimpleMessageConverter private MessageConverter messageConverter = new SimpleMessageConverter(); ... ... // setter:setMessageConverter // getter:getMessageConverter ... ... @Override public void convertAndSend(Object object) throws AmqpException { // 使用默认的 exchange、routingKey,而 CorrelationData 为空 convertAndSend(this.exchange, this.routingKey, object, (CorrelationData) null); } @Override public void convertAndSend(String exchange, String routingKey, final Object object, @Nullable CorrelationData correlationData) throws AmqpException { // send 方法发送的信息为 Message 类型,所以需要 convertMessageIfNecessary(object) send(exchange, routingKey, convertMessageIfNecessary(object), correlationData); } @Override public void send(final String exchange, final String routingKey, final Message message, @Nullable final CorrelationData correlationData) throws AmqpException { execute(channel -> { doSend(channel, exchange, routingKey, message, (RabbitTemplate.this.returnsCallback != null || (correlationData != null && StringUtils.hasText(correlationData.getId()))) && isMandatoryFor(message), correlationData); return null; }, obtainTargetConnectionFactory(this.sendConnectionFactorySelectorExpression, message)); } ... // 消息转换:Object -> Message protected Message convertMessageIfNecessary(final Object object) { if (object instanceof Message) { return (Message) object; } /** * 获取“消息转换器”并调用其 toMessage 方法 * 如果使用【Jackson2JsonMessageConverter】,则实际调用【AbstractMessageConverter.toMessage】 * * (Jackson2JsonMessageConverter 只包含构造方法,AbstractMessageConverter 是其父类的父类) */ return getRequiredMessageConverter().toMessage(object, new MessageProperties()); } ... private MessageConverter getRequiredMessageConverter() throws IllegalStateException { MessageConverter converter = getMessageConverter(); // 获取配置的 MessageConverter if (converter == null) { throw new AmqpIllegalStateException( "No 'messageConverter' specified. Check configuration of RabbitTemplate."); } return converter; } }
- 使用“消息转换器”:Jackson2JsonMessageConverter[相关 1] 需要配置(见:RabbitMQ:Jackson2JsonMessageConverter分析#应用配置)。
- 默认“消息转换器”:SimpleMessageConverter[其他 2]。
- AbstractMessageConverter.toMessage:
public abstract class AbstractMessageConverter implements MessageConverter { ... @Override public final Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { // 转换消息,genericType 为空(但此类的 genericType 并无作用) return toMessage(object, messageProperties, null); } @Override public final Message toMessage(Object object, @Nullable MessageProperties messagePropertiesArg, @Nullable Type genericType) throws MessageConversionException { MessageProperties messageProperties = messagePropertiesArg; if (messageProperties == null) { messageProperties = new MessageProperties(); } // 调用 createMessage 方法创建 Message 对象(实际的消息) Message message = createMessage(object, messageProperties, genericType); messageProperties = message.getMessageProperties(); // 保证 Message 的 messageProperties 中 MessageId 不为空 if (this.createMessageIds && messageProperties.getMessageId() == null) { messageProperties.setMessageId(UUID.randomUUID().toString()); } return message; } protected Message createMessage(Object object, MessageProperties messageProperties, @Nullable Type genericType) { return createMessage(object, messageProperties); // 可以看到 genericType 被抛弃了 } /** * 抽象方法,将会在子类中实现: * 如果实用【Jackson2JsonMessageConverter】,则实际调用【AbstractJackson2MessageConverter.createMessage】 */ protected abstract Message createMessage(Object object, MessageProperties messageProperties); }
- MessageProperties[相关 2] 用于保存消息的属性信息。
- AbstractJackson2MessageConverter.createMessage:
public abstract class AbstractJackson2MessageConverter extends AbstractMessageConverter implements BeanClassLoaderAware, SmartMessageConverter { ... public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; protected final ObjectMapper objectMapper; private MimeType supportedContentType; private String supportedCTCharset; @Nullable private ClassMapper classMapper = null; // !!! private Charset defaultCharset = DEFAULT_CHARSET; ... private Jackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); // !!! ... private boolean charsetIsUtf8 = true; ... // 构造方法(以下以 Jackson2JsonMessageConverter 的无参构造为例) protected AbstractJackson2MessageConverter(ObjectMapper objectMapper, MimeType contentType, String... trustedPackages) { Assert.notNull(objectMapper, "'objectMapper' must not be null"); Assert.notNull(contentType, "'contentType' must not be null"); this.objectMapper = objectMapper; // 为 new ObjectMapper()的对象 this.supportedContentType = contentType; // 为 "application/json" 的 MimeType(MIME 类型) this.supportedCTCharset = this.supportedContentType.getParameter("charset"); ((DefaultJackson2JavaTypeMapper) this.javaTypeMapper).setTrustedPackages(trustedPackages); } ... @Override protected Message createMessage(Object objectToConvert, MessageProperties messageProperties) throws MessageConversionException { // 创建 Message 对象,genericType 为空 return createMessage(objectToConvert, messageProperties, null); } @Override protected Message createMessage(Object objectToConvert, MessageProperties messageProperties, @Nullable Type genericType) throws MessageConversionException { byte[] bytes; // 通过 objectMapper 将 objectToConvert(要发送的对象)转换为 byte[] try { // 默认:以 Utf8(JsonEncoding.UTF8) 转换对象 if (this.charsetIsUtf8 && this.supportedCTCharset == null) { bytes = this.objectMapper.writeValueAsBytes(objectToConvert); } // 以设置的 supportedCTCharset 转换对象 else { String jsonString = this.objectMapper .writeValueAsString(objectToConvert); String encoding = this.supportedCTCharset != null ? this.supportedCTCharset : getDefaultCharset(); bytes = jsonString.getBytes(encoding); } } catch (IOException e) { throw new MessageConversionException("Failed to convert Message content", e); } /** * messageProperties:设置内容的 MIME 类型 * * supportedContentType由“构造方法、set方法”指定,默认:"application/json" */ messageProperties.setContentType(this.supportedContentType.toString()); /** * messageProperties:设置内容的字符集 * * supportedCTCharset由“构造方法、set方法”指定,默认:StandardCharsets.UTF_8 */ if (this.supportedCTCharset == null) { messageProperties.setContentEncoding(getDefaultCharset()); } // messageProperties:设置内容的长度 messageProperties.setContentLength(bytes.length); /** * messageProperties:【设置对象类型!!!】 * * 1、classMapper:只能由“set方法”指定,默认:空 * * 2、javaTypeMapper:只能由“set方法”指定,默认已设置为:DefaultJackson2JavaTypeMapper */ if (getClassMapper() == null) { //【通过 objectMapper 构建 JavaType 对象】 JavaType type = this.objectMapper.constructType( genericType == null ? objectToConvert.getClass() : genericType); if (genericType != null && !type.isContainerType() && Modifier.isAbstract(type.getRawClass().getModifiers())) { type = this.objectMapper.constructType(objectToConvert.getClass()); } /** * 通过 javaTypeMapper 的配置(Jackson2JavaTypeMapper 的实现类), * 将 JavaType 设置到 messageProperties * * 【默认将调用 DefaultJackson2JavaTypeMapper.fromJavaType】 */ getJavaTypeMapper().fromJavaType(type, messageProperties); } else { /** * 通过 classMapper 的配置(ClassMapper 的实现类), * 将 objectToConvert.getClass() 设置到 messageProperties */ getClassMapper().fromClass(objectToConvert.getClass(), messageProperties); // NOSONAR never null } return new Message(bytes, messageProperties); } }
- DefaultJackson2JavaTypeMapper[相关 6].fromJavaType:
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper { ... /** * 以下 addHeader、getClassIdFieldName、getContentClassIdFieldName、getKeyClassIdFieldName 方法 * 均已在父类 AbstractJavaTypeMapper 中实现 */ @Override public void fromJavaType(JavaType javaType, MessageProperties properties) { /** * 向 messageProperties 的【“__TypeId__”】中写入 javaType 的“原始类型”【即,对象本身的类型】 */ addHeader(properties, getClassIdFieldName(), javaType.getRawClass()); /** * 如果 javaType 表示的类型是“数组类型”以外的“容器类型”,【即,对象是一个容器类型】 * 向 messageProperties 的【“__ContentTypeId__”】中写入 javaType 元素的类型【即,对象的元素的类型】 * * P.S. “容器类型”:包括数组、映射和集合类型 */ if (javaType.isContainerType() && !javaType.isArrayType()) { addHeader(properties, getContentClassIdFieldName(), javaType.getContentType().getRawClass()); } /** * 如果 javaType 的 _keyType 不为空,【即,对象是一个映射类型】 * 向 messageProperties 的【“__KeyTypeId__”】中写入 javaType 的 _keyType 的类型【即,对象的 key 的类型】 * * P.S. _keyType 用于表示 MapLikeType(表示一个 Map-like 的类型)的“键的类型” */ if (javaType.getKeyType() != null) { addHeader(properties, getKeyClassIdFieldName(), javaType.getKeyType().getRawClass()); } } ... }
- AbstractJavaTypeMapper.addHeader:
public abstract class AbstractJavaTypeMapper implements BeanClassLoaderAware { ... // 用于保存 JavaType 的“原始类型” public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__"; // 用于保存 JavaType 的“元素的类型” public static final String DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__ContentTypeId__"; // 用于保存 JavaType 的“key 的类型” public static final String DEFAULT_KEY_CLASSID_FIELD_NAME = "__KeyTypeId__"; // 用于保存 String -> Class<?> 的映射 private final Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>(); // 用于保存 Class<?> -> String 的映射:由 idClassMapping 的键值反转得到 private final Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>(); ... public String getClassIdFieldName() { return DEFAULT_CLASSID_FIELD_NAME; } public String getContentClassIdFieldName() { return DEFAULT_CONTENT_CLASSID_FIELD_NAME; } public String getKeyClassIdFieldName() { return DEFAULT_KEY_CLASSID_FIELD_NAME; } // 设置 idClassMapping: public void setIdClassMapping(Map<String, Class<?>> idClassMapping) { this.idClassMapping.putAll(idClassMapping); createReverseMap(); } // 设置 classIdMapping:将 idClassMapping 的键值反转 private void createReverseMap() { this.classIdMapping.clear(); for (Map.Entry<String, Class<?>> entry : this.idClassMapping.entrySet()) { String id = entry.getKey(); Class<?> clazz = entry.getValue(); this.classIdMapping.put(clazz, id); } } ... protected void addHeader(MessageProperties properties, String headerName, Class<?> clazz) { /** * 向 MessageProperties.headers 中添加(“要发送对象的”)类型: * * 1、如果 classIdMapping 中存在 String -> Class<?> 的映射,则写入对应的 String * * 2、否则,写入 Class<?> 的名称 */ if (this.classIdMapping.containsKey(clazz)) { properties.getHeaders().put(headerName, this.classIdMapping.get(clazz)); } else { properties.getHeaders().put(headerName, clazz.getName()); } } }
相关
- ↑
Jackson2JsonMessageConverter 的类层次:
Jackson2JsonMessageConverter extends AbstractJackson2MessageConverter AbstractJackson2MessageConverter extends AbstractMessageConverter implements BeanClassLoaderAware, SmartMessageConverter AbstractMessageConverter implements MessageConverter
- Jackson2JsonMessageConverter:【类】只有构造方法,其他实用方法都在父类实现。
- 构造方法:用于指定 objectMapper、supportedContentType 等
- AbstractJackson2MessageConverter:【抽象类】但包括了几乎所有属性和方法。 —— 【核心】
- 属性:
- ClassMapper / Jackson2JavaTypeMapper:用于向 MessageProperties.headers 中写入“__TypeId__”等字段;
- 方法:(都已实现)
- createMessage:用于“Object -> Message”转换
- fromMessage:用于“Message -> Object”转换
- 属性:
- AbstractMessageConverter:【抽象类】只包括了部分方法(“Object -> Message”),其余在子类实现。
- 方法:
- toMessage:用于“Object -> Message”转换,将调用 createMessage 方法
- createMessage:(调用子类的 createMessage 方法)
- SimpleMessageConverter也间接继承了该抽象类。 ——【RabbitTemplate 默认的 MessageConverter】
- 方法:
- MessageConverter:【接口】消息转换器接口。
public interface MessageConverter { // Convert a Java object to a Message. Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException; default Message toMessage(Object object, MessageProperties messageProperties, @Nullable Type genericType) throws MessageConversionException { return toMessage(object, messageProperties); } // Convert from a Message to a Java object. Object fromMessage(Message message) throws MessageConversionException; }
public class Jackson2JsonMessageConverter extends AbstractJackson2MessageConverter { // 1、无参构造 public Jackson2JsonMessageConverter() { // 将调用 2、含参(trustedPackages)构造,并指定了 trustedPackages 为 "*" this("*"); } // 2、含参(trustedPackages)构造 public Jackson2JsonMessageConverter(String... trustedPackages) { // 将调用 4、含参(trustedPackages、trustedPackages)构造,并指定了 ObjectMapper 为 ObjectMapper 对象 this(new ObjectMapper(), trustedPackages); this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } // 3、含参(ObjectMapper)构造 public Jackson2JsonMessageConverter(ObjectMapper jsonObjectMapper) { // 将调用 4、含参(trustedPackages、trustedPackages)构造,并指定了 trustedPackages 为 "*" this(jsonObjectMapper, "*"); } // 4、含参(trustedPackages、trustedPackages)构造 public Jackson2JsonMessageConverter(ObjectMapper jsonObjectMapper, String... trustedPackages) { // 将调用父类(AbstractJackson2MessageConverter)的构造方法 // 并指定了父类的 supportedContentType 属性 ————(MessageProperties.CONTENT_TYPE_JSON 为 【"application/json"】) super(jsonObjectMapper, MimeTypeUtils.parseMimeType(MessageProperties.CONTENT_TYPE_JSON), trustedPackages); } }
- 如上,使用无参构造时:
- objectMapper 为 ObjectMapper 实例
- MimeType 为 "application/json"
- ↑ 2.0 2.1
MessageProperties:用于保存消息的各项属性
public class MessageProperties implements Serializable { ... public static final String CONTENT_TYPE_JSON = "application/json"; ... private final Map<String, Object> headers = new HashMap<>(); // 以 Map 保存的头信息 ... private String messageId; ... public Map<String, Object> getHeaders() { return this.headers; } ... }
- MessageProperties 是 Message 类的属性(二者都位于:“org.springframework.amqp.core”)。
- ↑
ObjectMapper:用于快速的进行各个类型和Json类型的相互转换。
ObjectMapper 提供了用于读取和写入 JSON 的功能,无论是从基本的 POJO(普通的旧 Java 对象)还是从通用的 JSON 树模型({@link JsonNode})读取和写入JSON,以及用于执行转换的相关功能。
- 位于:“com.fasterxml.jackson.databind”
- 参考:【Java ObjectMapper详解】
- ↑
JavaType:类型标记类的基类,用于包含信息和用作反序列化程序的键。
package com.fasterxml.jackson.databind; import ... public abstract class JavaType extends ResolvedType implements java.io.Serializable, // 2.1 java.lang.reflect.Type // 2.2 {
- 父类:java.lang.reflect.Type ——(参考:【Type接口:Java中的类型】)
- 子类:(“TypeBase”:
TypeBase extends JavaType implements JsonSerializable
)
Class Parent Description SimpleType TypeBase 简单类型:定义为除已识别的容器类型(数组、集合、映射)之外的任何类型 - 自定义的类,对应的都是 SimpleType
ArrayType TypeBase 数组类型表示 Java 数组。 ——包括“基本数组”和“对象值数组” - 此外,对象值数组可以具有任何其他元素类型。
CollectionType CollectionLikeType 表示 Java 集合类型(列表、集)的类型。 CollectionLikeType TypeBase 表示类似于集合类型的事物的类型。 ——不一定实现“{@link java.util.Collection}” - 这特别允许框架检查用于 Map 类型的配置和注释设置,并将其传递给可能更熟悉实际类型的自定义处理程序。
MapType MapLikeType 表示“真正的”Java 映射类型的类型。 MapLikeType TypeBase 表示类似映射类型的事物的类型,由键值对组成。 ——不一定实现“{@link java.util.Map}” - 但没有足够的内省功能来允许某种级别的泛型处理。
- 这特别允许框架检查用于 Map 类型的配置和注释设置,并将其传递给可能更熟悉实际类型的自定义处理程序。
PlaceholderForType TypeBase 侦测已解析类型的绑定时使用的帮助程序类型,这是专用化所必需的。 ResolvedRecursiveType TypeBase 用于自引用的内部占位符类型。 ReferenceType SimpleType 专用 {@link SimpleType},用于引用类型类型,即可以取消引用到不同类型的另一个值(或 null)的值。 - 引用的类型可以使用 {@link getContentType()} 访问。
- ↑
ClassMapper:(接口)用于在消息上设置元数据(MessageProperties),以便可以在接收消息时创建需要实例化的类。
public interface ClassMapper { void fromClass(Class<?> clazz, MessageProperties properties); Class<?> toClass(MessageProperties properties); }
- 子接口:
public interface Jackson2JavaTypeMapper extends ClassMapper {
- 实现类:
public class DefaultClassMapper implements ClassMapper, InitializingBean { public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper {
- ↑
DefaultJackson2JavaTypeMapper:
- AbstractJackson2MessageConverter(Jackson2JsonMessageConverter) 默认的 Jackson2JavaTypeMapper(ClassMapper)
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper implements Jackson2JavaTypeMapper { ... @Override public void fromJavaType(JavaType javaType, MessageProperties properties) { ... } @Override public JavaType toJavaType(MessageProperties properties) { ... } @Override public void fromClass(Class<?> clazz, MessageProperties properties) { fromJavaType(TypeFactory.defaultInstance().constructType(clazz), properties); } @Override public Class<?> toClass(MessageProperties properties) { return toJavaType(properties).getRawClass(); } }
应用配置[参考 1]
由以上过程可知(使用 Jackson2JsonMessageConverter 时): 1、Jackson2JsonMessageConverter 的构造方法用于指定 objectMapper、supportedContentType(消息的内容类型) 2、Jackson2JsonMessageConverter 的 Jackson2JavaTypeMapper(用于 JavaType 映射)默认为 DefaultJackson2JavaTypeMapper DefaultJackson2JavaTypeMapper 继承与 AbstractJavaTypeMapper(抽象类) AbstractJavaTypeMapper 中: 1、__TypeId__:用于指定 MessageProperties.headers 中保存“要发送对象的类型”的字段名称 2、idClassMapping:用于保存“Class -> String”的映射 MessageProperties.headers(Map<String, Object> 类型)的写入过程: 1、headers 的 key:写入“__TypeId__”; 2、headers 的 value:如果 AbstractJavaTypeMapper.idClassMapping 中有对应的映射,则写入对应 String,否则,写入“要发送对象的类型”的全限定名。
在消息的“生产者”和“消费者”之间传递消息,则必须:
- 依赖相同的 jar;
- 拥有相同(全限定名相同)的类;
- 获取 Message,然后自己解析;
对于“生产者”和“消费者”之间的解耦,必须: 1、配置 Jackson2JsonMessageConverter:设置 DefaultJackson2JavaTypeMapper 的 idClassMapping。 2、配置 Jackson2JsonMessageConverter:使用自定义的 ClassMapper(fromClass、toClass)和 JavaTypeMapper(fromJavaType、toJavaType)。
Jackson2JsonMessageConverter 配置:
- 方案一:设置 DefaultJackson2JavaTypeMapper 的 idClassMapping
- 生产者:
@Configuration public class RabbitConfiguration { @Bean public MessageConverter messageConverter() { Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); // Producer: String -> Class<?> Map<String, Class<?>> idClassMapping = new HashMap<>(); idClassMapping.put("Bean1", producer.Bean1.class); idClassMapping.put("Bean2", producer.Bean2.class); // ... more setting javaTypeMapper.setIdClassMapping(idClassMapping); messageConverter.setJavaTypeMapper(javaTypeMapper); return messageConverter; } ... }
- 消费者:
@Configuration public class RabbitConfiguration { @Bean public MessageConverter messageConverter() { Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); // Consumer: String -> Class<?> Map<String, Class<?>> idClassMapping = new HashMap<>(); idClassMapping.put("Bean1", consumer.Bean1.class); idClassMapping.put("Bean2", consumer.Bean2.class); // ... more setting javaTypeMapper.setIdClassMapping(idClassMapping); messageConverter.setJavaTypeMapper(javaTypeMapper); return messageConverter; } ... }
- 生产者:
- 方案二:使用自定义的 ClassMapper和 JavaTypeMapper
- 生产者:
@Configuration public class RabbitConfiguration { @Bean public MessageConverter messageConverter() { // Producer: Class<?> -> String Map<Class<?>, String> classIdMapping = new HashMap<Class<?>, String>(); classIdMapping.put(producer.Bean1.class, "Bean1"); classIdMapping.put(producer.Bean2.class, "Bean2"); // ... more setting Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); messageConverter.setClassMapper(new ClassMapper() { ... @Override public void fromClass(Class<?> clazz, MessageProperties properties) { // Set "__TypeId__" with classIdMapping properties.setHeader("__TypeId__", classIdMapping.get(clazz)); } }); return messageConverter; } ... }
- 消费者:
@Configuration public class RabbitConfiguration { @Bean public MessageConverter messageConverter() { // Consumer: String -> Class<?> Map<String, Class<?>> idClassMapping = new HashMap<>(); idClassMapping.put("Bean1", consumer.Bean1.class); idClassMapping.put("Bean2", consumer.Bean2.class); // ... more setting Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(); messageConverter.setClassMapper(new ClassMapper() { ... @Override public Class<?> toClass(MessageProperties properties) { // Get "__TypeId__" String typeId = (String) properties.getHeaders().get("__TypeId__"); // Get Class<?> with classIdMapping return idClassMapping.get(typeId); } }); return messageConverter; } ... }
- 生产者:
参考&其他
参考
其他
- ↑
JMS 的 MappinJackson2MessageConverter 配置:【分别在“生产者”、“消费者”中配置】
@Configuration public class JmsConfiguration { @Bean public MappingJackson2MessageConverter messageConverter(){ MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter(); // 1、设置: typeIdPropertyName messageConverter.setTypeIdPropertyName("_typeId"); // 添加 Map<String, Class<?>>:用于保存“String -> Class<?>”的映射 Map<String, Class<?>> typeIdMapping = new HashMap<>(); typeIdMapping.put("Bean1", bean1.class); typeIdMapping.put("Bean2", bean2.class); // 2、设置:typeIdMapping messageConverter.setTypeIdMappings(typeIdMapping); return messageConverter; } ... }
- 在“生产者”、“消费者”间传递消息时,会通过 Message.setStringProperty(String name, String value) 方法设置对象的类型,其中:
- name:为 “typeIdPropertyName 属性”的值,
- value:为(通过“classIdMappings 属性”映射的)表示对象类型的 String 值。
- MappingJackson2MessageConverter 类:
- 属性:
- typeIdPropertyName:保存“Message 中表示对象类型的字段的名称”; —— 没有默认值,必须设置
- idClassMappings:保存“String -> Class<?>”的映射;
- classIdMappings:保存“Class<?> -> String”的映射;
- 方法:
- setTypeIdPropertyName(...):用于设置 typeIdPropertyName 属性;
- setTypeIdMappings(...):用于设置 idClassMappings 和 classIdMappings 属性;
- toMessage(...):用于“Object -> Message”转换,需要 classIdMappings 属性;
- fromMessage(...):用于“Message -> Object”转换,需要 idClassMappings 属性;
- 该类位于“org.springframework.jms.support.converter”包(区别于“org.springframework.messaging.converter”包下的同名类)。
- 属性:
- ↑
SimpleMessageConverter:【RabbitTemplate 默认的 MessageConverter】
public class SimpleMessageConverter extends AllowedListDeserializingMessageConverter implements BeanClassLoaderAware { public static final String DEFAULT_CHARSET = "UTF-8"; private volatile String defaultCharset = DEFAULT_CHARSET; ... public void setDefaultCharset(String defaultCharset) { this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET; } ... /** * 由给定的 Object 创建 AMQP Message。 * * 如下,支持的载荷为:byte[]、String、Serializable */ @Override protected Message createMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { byte[] bytes = null; /** * 转换消息载荷(仅支持:byte[]、String、Serializable) */ if (object instanceof byte[]) { bytes = (byte[]) object; // messageProperties:设置内容的 MIME 类型为 "application/octet-stream" messageProperties.setContentType(MessageProperties.CONTENT_TYPE_BYTES); } else if (object instanceof String) { try { bytes = ((String) object).getBytes(this.defaultCharset); } catch (UnsupportedEncodingException e) { throw new MessageConversionException( "failed to convert to Message content", e); } // messageProperties:设置内容的 MIME 类型为 "text/plain" messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN); /// messageProperties:设置内容的字符集(由“set方法”指定,默认为:"UTF-8") messageProperties.setContentEncoding(this.defaultCharset); } else if (object instanceof Serializable) { try { bytes = SerializationUtils.serialize(object); } catch (IllegalArgumentException e) { throw new MessageConversionException( "failed to convert to serialized Message content", e); } // messageProperties:设置内容的 MIME 类型为 "application/x-java-serialized-object" messageProperties.setContentType(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT); } /** * 生成 AMQP Message 并返回 */ if (bytes != null) { // messageProperties:设置内容的长度 messageProperties.setContentLength(bytes.length); return new Message(bytes, messageProperties); } throw new IllegalArgumentException(getClass().getSimpleName() + " only supports String, byte[] and Serializable payloads, received: " + object.getClass().getName()); } @Override public Object fromMessage(Message message) throws MessageConversionException { ... } ... }
- 相同:都(间接)继承于“AbstractMessageConverter”。
- 不同:
- 不能在“生产者”、“消费者”间传递对象的类型。
- ——因为没有“ClassMapper”/“Jackson2JavaTypeMapper”属性,所以不会向 MessageProperties.headers 中写入“__TypeId__”等字段。
- 只支持:byte[]、String、Serializable 类型的消息载荷。
- 不能在“生产者”、“消费者”间传递对象的类型。