有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSpring,CGLIB:为什么不能代理泛型类?

我想问一下异常的根本原因:

Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to  
java.lang.reflect.ParameterizedType

在Spring中,当我们让Spring为类生成代理(即在事务管理器上设置proxy-target-class="true")时,会发生这种情况:

<tx:annotation-driven transaction-manager="transactionManager" 
proxy-target-class="true"/>

当要代理的类是参数化类时,即

public class SomeDAOImpl<SomeEntity> implements SomeDAO ...

例如,完整的故事可以在这个问题中阅读:Abstract DAO pattern and Spring's "Proxy cannot be cast to ..." problem!

问题:为什么Spring不能代理这个类?是因为它使用旧的代码生成库吗?因为类型擦除?如果SomeDAOImpl不是泛型类,它将成功(我检查了它)

请不要这样回答:“您应该代理接口,而不是类”。我知道


共 (4) 个答案

  1. # 1 楼答案

    这看起来像是Spring/CGLIB的问题,但问题实际上在您的构造函数中!我猜您正在尝试获取参数类型,以便将其提供给实体管理器(或您尝试使用的任何持久性框架)。我还猜,要做到这一点,您需要调用getGenericSuperclass()并将其转换为参数化类型

    该方法的行为方式取决于类。事实证明,并不是所有类对象都实现ParameterizedType接口(我同意,这很奇怪)。生成的CGLIB代理不保留泛型信息。它看起来像:

    public class SomeDAOImplProxy extends SomeDAOImpl
    

    我不知道CGLIB代理为什么不保留泛型,因为CGLIB代理的细节非常复杂。这可能是根本不可能做到的。也可能是这样做对他们来说不是优先事项

    下面是一个简单的实验,它去掉了Spring和CGLIB,但仍然得到了那个错误

    public static void main(String [] args) {
        List<Object> fooList = new ArrayList<Object>();
        String fooString = "";
        System.out.println((ParameterizedType)fooList.getClass().getGenericSuperclass());
        System.out.println((ParameterizedType)fooString.getClass().getGenericSuperclass());
    }
    
  2. # 2 楼答案

    我用这段代码解决了这个问题:

    Type t = getClass();
    do {
      t = ((Class<T>) t).getGenericSuperclass();
    } while (!(t instanceof ParameterizedType));
    ParameterizedType pt = (ParameterizedType) t;
    actualGenericType = (Class<T>) pt.getActualTypeArguments()[0];
    

    我认为这是一个更干净的解决方案,因为它不使用CGLIB的内部结构

  3. # 3 楼答案

    我们使用类似于James's answer的解决方案,但更一般一点:

    Type genericSuperClass = repositoryClass.getGenericSuperclass();
    ParameterizedType parametrizedType;
    if (genericSuperClass instanceof ParameterizedType) { // class
        parametrizedType = (ParameterizedType) genericSuperClass;
    } else if (genericSuperClass instanceof Class) { // in case of CGLIB proxy
        parametrizedType = (ParameterizedType) ((Class<?>) genericSuperClass).getGenericSuperclass();
    } else {
        throw new IllegalStateException("class " + repositoryClass + " is not subtype of ParametrizedType.");
    }
    
    @SuppressWarnings("unchecked")
    Class<T> entityClass = (Class<T>) parametrizedType.getActualTypeArguments()[0];
    return entityClass;
    
  4. # 4 楼答案

    由于CGLIB对类进行子类化以生成代理,因此必须获得超类的泛型超类才能获得^{

    public class MyCglibProxiedBean<T> {
    
        private final Class<T> paramClass;
    
        public MyCglibProxiedBean() {
            Type genericSuperClass = getClass().getGenericSuperclass();
    
            // Get the generic super class of the super class if it's a cglib proxy
            if (getClass().getName().contains("$$EnhancerByCGLIB$$")) {
                genericSuperClass = getClass().getSuperclass().getGenericSuperclass();
            }
    
            this.paramClass = (Class<T>) ((ParameterizedType) genericSuperClass).getActualTypeArguments()[0];
        }
    }