【问题标题】:Spring, CGLIB: why can't a generic class be proxied?Spring,CGLIB:为什么不能代理泛型类?
【发布时间】:2011-12-12 13:08:29
【问题描述】:

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

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 不是泛型类,它会成功(我检查过)。

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

【问题讨论】:

  • 这个问题也可能是相关的——stackoverflow.com/questions/3969874/…
  • @DonRoby 我希望它这么简单......我确实有一个公共的非参数构造函数。 CGLIB 的解释:此问题的常见原因包括使用最终类或不可见类,这里也不是问题。

标签: java spring cglib


【解决方案1】:

这看起来像是 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());
}

【讨论】:

  • 我不知道你是先知,还是办公室里坐在我后面的人之一,因为你的猜测是 100% 正确的。非常感谢。
【解决方案2】:

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

【讨论】:

    【解决方案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】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-24
        • 1970-01-01
        • 2019-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多