【问题标题】:Abstract DAO pattern and Spring's "Proxy cannot be cast to ..." problem!抽象 DAO 模式和 Spring 的“Proxy cannot be cast to ...”问题!
【发布时间】:2011-04-20 15:14:44
【问题描述】:

我知道这个问题经常被问到,但我找不到可行的解决方案:

这是我的 AbstractDAO:

public interface AbstractDao<T>
{
  public T get(Serializable id);
  //other CRUD operations
}

这是我的 JPA 的实现:

public abstract class AbstractDaoJpaImpl<T> implements AbstractDao<T> , Serializable
{
  protected EntityManager em;

  protected Class<T> clazz;

  @SuppressWarnings("unchecked")
  public AbstractDaoJpaImpl()
  {
    ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
    this.clazz = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
  }

  public abstract void setEntityManager(EntityManager em);  
  //implementations skipped
}

这是一个实体的道:

public interface PersonDao extends AbstractDao<Person>
{
  //empty
}

这是它的实现:

@Repository
public class PersonDaoImpl extends AbstractDaoJpaImpl<Person> implements PersonDao , OtherInterface
{
  @PersistenceContext(unitName="company")
  @Override
  public void setEntityManager(EntityManager em)
  {
    this.em = em;
  }

  @Override // implements OtherInterface.additionalMethods()
  public additionalMethods()
  {
    // implements...
  }
}

整个架构很简单:

接口AbstractDao定义了简单的CRUD方法。

接口PersonDao扩展了AbstractDAO,没有任何插件方法。

AbstractDaoJpaImpl 定义了 JPA 的 AbstractDao 实现

class PersonDaoImpl 扩展了 AbstractDaoJpaImpl 并实现了 PersonDao AND OtherInterface ,它添加了附加方法()...

如果,PersonDaoImpl 只实现了 PersonDao ,没有实现 OtherInterface.additionalMethods() ,一切正常。

我可以使用

<tx:annotation-driven transaction-manager="transactionManager" /> 

在我的 spring 的 XML 文件中。

但是,PersonDaoImpl 实现了 OtherInterface(s) ,在测试/运行时,我必须将 DAO 从 PersonDao 转换为 PersonDaoImpl 或 OtherInterfaces,例如:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:app.xml"})
@TransactionConfiguration(transactionManager="transactionManager" , defaultRollback=false)
public class PersonDaoTest
{
  @Inject 
  PersonDao dao;

  @Test
  public void testAdditionalMethod()
  {
    PersonDaoImpl impl = (PersonDaoImpl) dao;
    System.out.println(impl.additionalMethod(...));
  }
}

问题发生在(PersonDaoImpl) dao 时,抛出“Proxy cannot be cast to PersonDaoImpl”异常:

java.lang.ClassCastException: $Proxy36 cannot be cast to foobar.PersonDaoImpl
    at foobar.PersonDaoTest.testAdditionalMethod(PersonDaoTest.java:36)

这个是googleing的时候经常问的,大家建议在&lt;tx:annotation-driven&gt;后面加proxy-target-class="true"

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"  />

这将使用 CGLIB 而不是 JDK 的动态代理。

但是在初始化 Spring 时它会引发另一个异常:

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

在 AbstractDaoJpaImpl 的构造函数中:

ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();

每个问题都到此为止,我现在找不到任何有效的解决方案。

谁能给我一个可行的解决方案? 非常感谢!

环境:Spring-3.0.4、javaee-api-6.0、javax.inject、cglib-2.2、hibernate-jpa-2.0-api-1.0.0、

【问题讨论】:

    标签: java spring cglib genericdao


    【解决方案1】:

    你解决了错误的问题。代理 bean 并不意味着以一种或另一种方式转换为原始类。这将打破依赖注入的全部意义。毕竟:当您将依赖项指定为接口时,您请求的是一个满足合同的 bean,而不是实现细节。将其转换为原始 bean 类会破坏这种松散耦合。

    您是说其他方法由您称为OtherInterface 的接口支持,那么为什么不使用它呢?毕竟,代理将实现所有目标类的接口,而不仅仅是注入的。

    @Test
    public void testAdditionalMethod()
    {
        OtherInterface oi = (OtherInterface) dao;
        System.out.println(oi.additionalMethod(...));
    }
    

    基本上你有这些选项(从干净到脏排序):

    1. 分离您的关注点和使用 不同的豆子用于不同的 接口
    2. 创建一个扩展的元接口 OtherInterfacePersonDao 和 让你的 bean 实现它 元接口
    3. 将 bean 投射到接口 您随时需要。

    【讨论】:

    • 谢谢,我应该@Inject OtherInterface otherInterfaceImpl;并针对 otherInterfaceImpl 进行测试。它有效!
    【解决方案2】:

    是的,spring 总是创建代理类,这就是它实际上如何通过 xml 配置发现非侵入式编织和 aop...尝试在 spring 文档中搜索该错误,应该有规则可以遵循和解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多