首先说一句我打脸了,Spring-AMQP 1.6之后就支持参数类型推断了,不需要这样麻烦,只要升级就行了。这套逻辑是我为了Spring-AMQP 1.3.9写的,但是最近写文章的时候,是看的Spring-AMQP-1.7.4的源码(新旧项目兼容,我是为了旧项目写的功能,但是写文章到时候看的是新的项目,尴尬)。我写的时候还在想为什么代码不跟印象里不一样了, 由于实在太晚了,就大概看看,没有仔细研究。今天2020-06-14,在研究写方案三的时候,经过仔细研读源码,发现了疏漏,就赶紧改一下,省的误导大家。
如果大家无法升级Spring-AMQP,还得用老版本,这个文章对大家还是有帮助的。
序
最近又充满的冲劲,打算再把博客捡起来,(っ•̀ω•́)っ✎⁾⁾ 我爱学习
原由
Spring-AMQP 支持 生产者 使用自定义的 Bean,然后通过 MessageConverter
转换成Message
进行发送。消费者 也可以通过 MessageConverter
将 Message
再转化成自定义类。毕竟 Message
构建解析还是挺麻烦的。
MessageConverter
有多个子类:
- 通过 Java
Serializable
,String
,纯byte[]
进行传递 :SerializerMessageConverter
、SimpleMessageConverter
- 通过 Xml 进行传递:
MarshallingMessageConverter
- 通过 Json 进行传递:
JsonMessageConverter
、Jackson2JsonMessageConverter
- 包装其他的
MessageConverter
,根据 contentType 进行转发:ContentTypeDelegatingMessageConverter
其中Jackson2JsonMessageConverter
比较常用,使用 Jackson 2 进行转换(以下简称 JsonMessageConverter
)。但是 MessageConverter
这个接口有个小问题,它为了封装最通用的转换逻辑,其中 fromMessage
只有一个参数,这导致无法获取 自定义 Bean 的 Class
,消费者 就无法通过反射构建 Bean。
// 将自定义对象转成Message, 生产者
Message toMessage(Object object, MessageProperties messageProperties)
// 将Message转成自定义对象, 消费者
Object fromMessage(Message message)
所以JsonMessageConverter
通过实现 AMQP 协议,填充 headers 中 __TypeId__ 的值,来传递 Class
的Name。 它 的默认是 填充包括的包名的全称,说的直白点,那就是在不对它进行任何改造的前提下,发送消息的 Bean 和接受消息的 Bean 必须是一样的,不仅是要里面的字段一样,类名一样,连包名都要一样。
所以当 生产者 使用 JsonMessageConverter
发送消息时,消费者 可以有如下几种方式来接收:
- 消费者 和 生产者 依赖相同的 jar。
- 不依赖jar,那就在自己系统建一个相同的
Class
,包名,类名都得相同。 - 获取
Message
,然后自己解析。
其中方案1,方案2 系统之间耦合性太强了,方案3 是个还可以方案,但是我被 SpringMVC 给惯坏了,感觉还是有点麻烦,就想自动转换成 Bean , 然后直接使用就行了。那我就开始研究源码,找到解决方案。
思路
上面已经说了,JsonMessageConverter
是通过填充 __TypeId__ 来实现 json 序列化和反序列化的,那我就找到对应的代码, 修改 __TypeId__ 就行了。虽然说得简单,但是我也是一步步分析源码找到的,接下来讲解我是如何一步步找到对应源码的。
消费者源码分析
首先我根据经验推断,从Message
转成 Bean 的时候,必须知道对应 Bean 的Class
,不然不可能实现转换。然后找到了这段代码,特别好理解, targetJavaType 和 targetClass 明确的“暗示”它的作用,毕竟我也是手动创建 并使用过 ObjectMapper
的人(✪ω✪)。
那咱们打开 函数 convertBytesToObject 来验证猜测,看到了熟悉的代码,证明我们的猜测,jsonObjectMapper 就是 Jackson 2 ObjectMapper
的实例。
看到这块我特别的开心,这说明JsonMessageConverter
没有什么神秘的,跟 咱们平时 通过 Class
来解析 Json 没有什么区别,咱们接下去的目标,就是找到 Class 是如何获取的。
@Override
public Object fromMessage(Message message)
throws MessageConversionException {
// ……省略无关代码……
if (getClassMapper() == null) {
JavaType targetJavaType = getJavaTypeMapper()
.toJavaType(message.getMessageProperties());
content = convertBytesToObject(message.getBody(),
encoding, targetJavaType);
}
else {
Class<?> targetClass = getClassMapper().toClass(
message.getMessageProperties());
content = convertBytesToObject(message.getBody(),
encoding, targetClass);
}
// ……省略无关代码……
}
private Object convertBytesToObject(byte[] body, String encoding, JavaType targetJavaType) throws IOException {
String contentAsString = new String(body, encoding);
return this.jsonObjectMapper.readValue(contentAsString, targetJavaType);
}
private Object convertBytesToObject(byte[] body, String encoding, Class<?> targetClass) throws IOException {
String contentAsString