Jackson2JsonMessageConverter是如何推断出来需要反序列化为哪种Java类型

62 阅读3分钟

下面是消息反序列化的代码,这里可以看到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,这个inferredTypeJavaType类型的

// 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);
}

methodinferredArgumentType究竟是怎么来的呢?

这个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,源头是我们定义的监听器方法上的参数类型。