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

时间:2011-12-12 13:08:29

标签: java spring cglib

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

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

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

<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 个答案:

答案 0 :(得分:5)

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

方法的行为方式取决于类。事实证明,并非所有Class对象都实现了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());
}

答案 1 :(得分:4)

由于CGLIB将您的类子类化为生成代理,您必须获取超类的通用超类才能获得ParameterizedType

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];
    }
}

答案 2 :(得分:1)

我们使用类似于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;

答案 3 :(得分:0)

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

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

我认为这是一个更清洁的解决方案,因为它没有使用CGLIB的内部结构。