关于泛型T的类型动态获取

定义一个接口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对象。

这个方法在以下场景中特别有用:

  1. 判断对象类型:当你需要判断一个对象是否是某个特定类型的实例时,但由于该对象可能是一个代理对象,直接使用instanceof关键字可能会得到错误的结果。通过使用AopUtils.getTargetClass(),你可以获取到对象的原始类,然后再进行类型判断。
  2. 访问目标对象的方法或属性:有时,你可能需要直接访问目标对象的特定方法或属性,而不是通过代理对象。通过获取目标类的Class对象,你可以使用反射来调用这些方法或访问这些属性。
  3. 在切面中处理特定类型的目标对象:在编写切面时,你可能需要根据目标对象的类型来执行不同的逻辑。通过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循环来不断向上寻找,找到实现的接口。

后面的逻辑就很好理解,不多说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值