我想问一下异常的根本原因:
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
不是通用类,那么它会成功(我检查过)。
请不要回答:“你应该代理接口,而不是类”。我知道。
答案 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的内部结构。