定义一个接口ErpMqListener<T>,现有实现类ErpMqListenerImpl implements ErpMqListener<String>,现在希望根据ErpMqListenerImpl在运行时动态的获取泛型T的实际类型String.class。
先直接放代码
public static Class<?> getMessageType(ErpMqListener erpMessageListener) {
Class<?> targetClass = AopUtils.getTargetClass(erpMessageListener);
Type[] interfaces = targetClass.getGenericInterfaces();
Class<?> superclass = targetClass.getSuperclass();
while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) {
interfaces = superclass.getGenericInterfaces();
superclass = targetClass.getSuperclass();
}
if (Objects.nonNull(interfaces)) {
for (Type type : interfaces) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (Objects.equals(parameterizedType.getRawType(), ErpMqListener.class)) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {
return (Class) actualTypeArguments[0];
} else {
return Object.class;
}
}
}
}
}
return Object.class;
}
一行行的看
AopUtils.getTargetClass
首先,我们需要了解Spring AOP(面向切面编程)的基本概念。在Spring框架中,AOP允许开发者定义横切关注点(cross-cutting concerns),这些关注点是应用程序中多个位置都需要处理的通用功能,比如日志记录、事务管理、安全性等。通过将这些关注点与主要业务逻辑分离,AOP有助于提高代码的可维护性和可重用性。
为了实现AOP,Spring创建了代理对象,这些代理对象包含了目标对象(即主要业务逻辑)和切面逻辑(即横切关注点)。代理对象在运行时拦截对目标对象的调用,并在必要时执行切面逻辑。
现在,让我们来看看AopUtils.getTargetClass()
方法的具体作用。这个方法接受一个对象作为参数,并尝试确定这个对象是否是Spring AOP代理对象。如果是代理对象,该方法将返回被代理的原始目标类的Class
对象;如果不是代理对象,它将直接返回传入对象的类的Class
对象。
这个方法在以下场景中特别有用:
- 判断对象类型:当你需要判断一个对象是否是某个特定类型的实例时,但由于该对象可能是一个代理对象,直接使用
instanceof
关键字可能会得到错误的结果。通过使用AopUtils.getTargetClass()
,你可以获取到对象的原始类,然后再进行类型判断。 - 访问目标对象的方法或属性:有时,你可能需要直接访问目标对象的特定方法或属性,而不是通过代理对象。通过获取目标类的
Class
对象,你可以使用反射来调用这些方法或访问这些属性。 - 在切面中处理特定类型的目标对象:在编写切面时,你可能需要根据目标对象的类型来执行不同的逻辑。通过
AopUtils.getTargetClass()
,你可以在切面中获取到目标对象的原始类,并根据这个类来编写条件逻辑。
当然,如果你没有使用截面,可以直接
Class<?> targetClass = erpMessageListener.getClass();
Type[] interfaces = targetClass.getGenericInterfaces();
编译后的字节码会把所有泛型信息擦除(泛型擦除),也就是说,无法通过反射的办法拿到参数。但是Java提供了其他方法。getGenericInterfaces()方法就是拿到当前Class实现的所有接口信息。
带有泛型的Type是ParameterizedType类型,不带有泛型的Type是Class类型,可以参考下面代码
for (Type type : interfaces) {
if (type instanceof ParameterizedType) {
System.out.println("带有泛型:" + type.getTypeName());
} else if (type instanceof Class){
System.out.println("非泛型:" + type.getTypeName());
} else {
System.out.println("N:" + type.getTypeName());
}
}
while循环
Interface A带有泛型T,其被Class B实现并定义Interface A泛型T为String.class,Class C继承Class B。此时Class C调用getGenericInterfaces()是没有任何返回的,因为它没有实现任何接口。所以就需要用一个while循环来不断向上寻找,找到实现的接口。
后面的逻辑就很好理解,不多说了。