1

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

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 ..." question!

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

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

4

4 回答 4

5

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

该方法的行为方式取决于类。事实证明,并非所有 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());
}
于 2011-12-12T13:42:18.920 回答
5

由于 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];
    }
}
于 2012-12-17T19:49:15.580 回答
1

我们使用类似于詹姆斯答案的解决方案,但恕我直言,更通用一点:

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;
于 2014-01-02T11:55:38.343 回答
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 的内部结构。

于 2016-05-11T12:05:37.180 回答