下面是消息反序列化的代码,这里可以看到convertBytesToObject
方法是根据targetJavaType
(这是一个JavaType
类型的对象,JavaType
是Jackson中进行反序列化时使用的)来进行反序列化的,那targetJavaType
是怎么得到的呢?
JavaType targetJavaType = getJavaTypeMapper()
.toJavaType(message.getMessageProperties());
content = convertBytesToObject(message.getBody(),
encoding, targetJavaType);
从org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper#toJavaType
中看到需要获取inferredType
,这个inferredType
是JavaType
类型的
// org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper#toJavaType
@Override
public JavaType toJavaType(MessageProperties properties) {
// 获取inferredType,这个inferredType是JavaType类型的
JavaType inferredType = getInferredType(properties);
if (inferredType != null
&& ((!inferredType.isAbstract() && !inferredType.isInterface()
|| inferredType.getRawClass().getPackage().getName().startsWith("java.util")))) {
return inferredType;
}
......
}
继续看往下看,一直到这个方法,TypeFactory.defaultInstance().constructType
方法根据输入的Type
构造出对应的JavaType
进行输出。因此可以知道通过properties.getInferredArgumentType()
肯定是可以得到一个Type
类型的
protected JavaType fromInferredTypeHeader(MessageProperties properties) {
return TypeFactory.defaultInstance().constructType(properties.getInferredArgumentType());
}
public JavaType constructType(Type type) {
return _fromAny(null, type, EMPTY_BINDINGS);
}
至此,根据TypeFactory.defaultInstance().constructType
方法我们知道了要想知道targetJavaType
是怎么来的,得先知道properties.getInferredArgumentType()
是怎么来的。
接下来,我们就去寻找properties
上的inferredArgumentType
(推断参数类型)是怎么来的。
从下面消费者提取消息的源码,我们知道了,当this.method
不为空的时候,会执行messageProperties.setInferredArgumentType(this.inferredArgumentType)
,此时就会给inferredArgumentType
赋值了,赋值为this.inferredArgumentType
。
#org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.MessagingMessageConverterAdapter#extractPayload
protected Object extractPayload(org.springframework.amqp.core.Message message) {
MessageProperties messageProperties = message.getMessageProperties();
if (this.bean != null) {
messageProperties.setTargetBean(this.bean);
}
if (this.method != null) {
messageProperties.setTargetMethod(this.method);
if (this.inferredArgumentType != null) {
messageProperties.setInferredArgumentType(this.inferredArgumentType);
}
}
return extractMessage(message);
}
那method
和inferredArgumentType
究竟是怎么来的呢?
这个method其实就是我们定义的监听器方法。此时我们就必须从org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.MessagingMessageConverterAdapter
说起了。每一个监听器方法都会被定义成一个MessagingMessageConverterAdapter
。我们看下MessagingMessageConverterAdapter
的构造方法
MessagingMessageConverterAdapter(Object bean, Method method, boolean batch) {
// 消费者类
this.bean = bean;
// 消费者方法
this.method = method;
this.isBatch = batch;
// 推断参数类型
this.inferredArgumentType = determineInferredType();
if (logger.isDebugEnabled() && this.inferredArgumentType != null) {
logger.debug("Inferred argument type for " + method.toString() + " is " + this.inferredArgumentType);
}
}
通过这个构造方法我们知道method其实是我们定义的监听器方法。
那inferredArgumentType
呢?我们看下determineInferredType()
方法。该方法会根据监听器方法上设置的参数来确定推断参数类型。从这个方法我们也可以知道,如果监听器设置了多个接收消息的参数,就无法推断出来类型了,会return null。
// 私有方法声明,用于确定推断类型。
private Type determineInferredType() {
// 如果方法为空,则没有类型可以推断,返回null。
if (this.method == null) {
return null;
}
Type genericParameterType = null;
// 遍历方法的所有参数。
for (int i = 0; i < this.method.getParameterCount(); i++) {
// 对于每个参数,创建一个方法参数对象。
MethodParameter methodParameter = new MethodParameter(this.method, i);
/*
* 下面找出一个单一的无注解或带有@Payload注解的参数。
* 忽略类型为Message的参数,因为它们与类型转换无关。
*/
// 如果参数符合条件并且(其上没有注解或者具有@Payload注解)
if (isEligibleParameter(methodParameter)
&& (methodParameter.getParameterAnnotations().length == 0
|| methodParameter.hasParameterAnnotation(Payload.class))) {
// 如果genericParameterType为null,这是第一个符合条件的参数。
if (genericParameterType == null) {
genericParameterType = extractGenericParameterTypFromMethodParameter(methodParameter);
}
else {
// 如果已经有了一个符合条件的参数,又发现了另一个,那么就存在歧义,无法确定要推断的类型。
// 这种情况下,记录调试日志并返回null。
if (MessagingMessageListenerAdapter.this.logger.isDebugEnabled()) {
MessagingMessageListenerAdapter.this.logger
.debug("Ambiguous parameters for target payload for method " + this.method
+ "; no inferred type header added");
}
return null;
}
}
}
// 返回推断的参数类型,或者null如果没有找到符合条件的参数。
return genericParameterType;
}
到这里,我们的所有疑问就都解决了。convertBytesToObject
方法是根据targetJavaType来进行反序列化的。targetJavaType通过调用TypeFactory.defaultInstance().constructType(properties.getInferredArgumentType())
生成,入参properties.getInferredArgumentType()
的源头是我们定义的监听器方法上的参数类型。因此convertBytesToObject
方法反序列化时用到的targetJavaType
,源头是我们定义的监听器方法上的参数类型。