摘要DAO模式和Spring的“代理无法转换为......”问题!

时间:2010-10-04 02:56:20

标签: java spring cglib genericdao

我知道这经常被问到,但我找不到合适的解决方案:

这是我的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。

class AbstractDaoJpaImpl 定义了JPA的AbstractDao实现

PersonDaoImpl 扩展了AbstractDaoJpaImpl并实现了PersonDao AND OtherInterface,它添加了aditionalMethods() ......

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

我可以用

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

在我的spring的XML文件中。

但是,PersonDaoImpl实现了OtherInterface,在测试/运行时,我必须将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引发“代理无法转换为PersonDaoImpl”异常时,会出现问题:

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

这经常被问到googleing时,每个人都建议将proxy-target-class="true"添加到<tx:annotation-driven>

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

2 个答案:

答案 0 :(得分:12)

你正在解决错误的问题。代理豆并不意味着以某种方式投射到原始类。这将打破依赖注入的整个点。毕竟:当您将依赖项指定为接口时,您正在请求满足合同的bean,而不是实现细节。将它转换为原始的bean类可以打破这种松散的耦合。

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

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

基本上你有这些选项(从清洁到脏):

  1. 分开您的顾虑和使用 不同的豆子为不同 接口
  2. 创建扩展的元接口 OtherInterfacePersonDao和。{ 让你的bean实现它 metainterface
  3. 将bean转换为接口 你需要在任何特定的时刻。

答案 1 :(得分:0)

是的,春天总是会创建代理类,这就是它实际上是如何通过xml配置发现非侵入式编织和aop ...尝试在春季文档中搜索该错误,其中包含了遵循和解决的规则。