相关背景
最近公司新项目用到了RocketMQ,为了更便捷的使用,便对RocketMQ的客户端进行了二次封装,在二次封装的过程中,出现了一个诡异问题,如下
public interface RocketMQListener<T> {
void onMessage(T message);
}
public abstract class EnhanceMessageHandler<T extends BaseMessage> implements RocketMQListener<T>{
@Override
public final void onMessage(T message) {
// 一些其他操作
handleMessage(message);
// 一些其他操作
}
abstract void handleMessage(T message);
}
public class BusinessMessageHandler extends EnhanceMessageHandler<BusinessMessage>{
@Override
void handleMessage(BusinessMessage message) {
// 业务处理
}
}
首先,定义了一个抽象类EnhanceMessageHandler继承RocketMQListener, 重写了onMessage方法,在其中进行一些操作,最终消息交给handleMessage方法来处理,该方法由具体的业务Hanndler来实现。
当完成这个功能时,项目启动失败,报错如下:
Caused by: java.lang.RuntimeException: parameterType:T of onMessage method is not supported
我又去rocketmq的github项目,找了一下相关issue,发现从去年6月开始多人遇到这个问题,但是一直未修复,不知道原因为何?
相关issue github.com/apache/rock…
于是,我自己翻看了一下RocketMq相关代码,找到了问题所在,要想弄清楚这个问题,就需要一些泛型的知识,对泛型比较熟的可以忽略本小结,直接看结论。
相关知识
jdk1.5引入了泛型,因此也在jdk中新增了一个接口 java.lang.reflect.Type, 在Class类中增加了三个方法getGenericSuperclass()、getGenericInterfaces()、getTypeParameters()
Type接口
Type是Java编程语言中所有类型的公共超接口。其中包括原始类型、参数化类型、数组类型、类型变量和原始类型。
其派生的子接口有:
GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType
派生的类有:
Class
| 派生的类或接口 | 说明 |
|---|---|
| java.lang.Class | Java 类 API,如 java.lang.String |
| java.lang.reflect.GenericArrayType | 泛型数组类型 |
| java.lang.reflect.ParameterizedType | 泛型参数类型 |
| java.lang.reflect.TypeVariable | 泛型类型变量,如Collection 中的 E |
| java.lang.reflect.WildcardType | 泛型通配类型 |
相关方法
getGenericSuperclass方法
// 返回父类的Type类型
public Type getGenericSuperclass()
getGenericInterfaces方法
// 返回该类直接实现的接口的Type类型数组
public Type[] getGenericInterfaces() {}
getTypeParameters方法
// 返回该类上的泛型声明的TypeVariable数组
public TypeVariable<Class<T>>[] getTypeParameters(){}
DEMO代码
通过运行下面一段代码,来熟悉一下相关API
public class App {
public static void main(String[] args) {
BusinessMessageHandler businessMessageHandler = new BusinessMessageHandler();
Class clazz = businessMessageHandler.getClass();
System.out.println();
System.out.println("======================EnhanceMessageHandler============================");
ParameterizedType enhanceMessageHandlerType = (ParameterizedType) clazz.getGenericSuperclass();
System.out.println("enhanceMessageHandlerType: " + enhanceMessageHandlerType);
TypeVariable enhanceMessageHandlerTypeVariable = ((Class) enhanceMessageHandlerType.getRawType()).getTypeParameters()[0];
System.out.println("enhanceMessageHandlerTypeVariable: " + enhanceMessageHandlerTypeVariable);
Type enhanceMessageHandlerActualTypeArgument = enhanceMessageHandlerType.getActualTypeArguments()[0];
System.out.println("enhanceMessageHandlerTypeArgument: " + enhanceMessageHandlerActualTypeArgument);
System.out.println();
System.out.println("=====================RocketMqListener================================");
ParameterizedType rocketMqListenerType = (ParameterizedType) ((Class) enhanceMessageHandlerType.getRawType()).getGenericInterfaces()[0];
System.out.println("rocketMqListenerType: " + rocketMqListenerType);
TypeVariable rocketMqListenerTypeVariable = ((Class) rocketMqListenerType.getRawType()).getTypeParameters()[0];
System.out.println("rocketMqListenerTypeVariable: " + rocketMqListenerTypeVariable);
// 获取ParameterizedType中的泛型参数
TypeVariable rocketMqListenerTypeArgument = (TypeVariable) rocketMqListenerType.getActualTypeArguments()[0];
System.out.println("rocketMqListenerTypeArgument: " + rocketMqListenerTypeArgument);
}
}
打印结果
getTypeParameter和getActualTypeArguments方法的区别是: getTypeParameter返回的是该Class生命的泛型类型,getActualTypeArguments是其该类的子类定义的该泛型的真正类型。
结论
在org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer该类中,使用getMessageType对RocketMQListener和RocketMQReplyListener上通过泛型声明的Message类型进行了解析。
其逻辑是:
- 首先找到RocketMQListener或RocketMQReplyListener接口的Type类型
- 调用该Type类型的getActualTypeArguments方法,获取ActualTypeArguments
在我们这个场景中,actualTypeArguments[0]是T,其真正的Class类型在EnhanceMessageHandler的获取ActualTypeArguments中,所以出错。