簡體   English   中英

CGLIB春季:為什么不能代理泛型類?

[英]Spring, CGLIB: why can't a generic class be proxied?

我想問一下異常的根本原因

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 ...

例如,可以在以下問題中閱讀全文: 抽象DAO模式和Spring的“代理不能轉換為...”問題!

問題: Spring為什么不能代理此類? 是因為它使用了舊的代碼生成庫? 因為類型擦除? 如果SomeDAOImpl不是通用類,它將成功(我檢查過)。

請不要這樣回答:“您應該代理接口,而不是類”。 我知道。

這看起來像一個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());
}

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

我們使用的解決方案與James的答案類似,但恕我直言有點籠統:

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;

我設法用以下代碼解決了這個問題:

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

我認為這是一個更干凈的解決方案,因為它不使用CGLIB的內部結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM