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

我知道这经常被问到,但我找不到一个有效的解决方案:

这是我的AbstractDAO:

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

这是我的JPA实现:

 public abstract class AbstractDaoJpaImpl implements AbstractDao , Serializable { protected EntityManager em; protected Class clazz; @SuppressWarnings("unchecked") public AbstractDaoJpaImpl() { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); this.clazz = (Class) genericSuperclass.getActualTypeArguments()[0]; } public abstract void setEntityManager(EntityManager em); //implementations skipped } 

这是一个实体的道:

 public interface PersonDao extends AbstractDao { //empty } 

这是它的实现:

 @Repository public class PersonDaoImpl extends AbstractDaoJpaImpl 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实现

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

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

我可以用

  

在我的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抛出“Proxy无法强制转换为PersonDaoImpl”exception时,会出现问题:

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

googleing时经常会问这个问题,每个人都建议在添加proxy-target-class="true"

  

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

但是在初始化Spring时会抛出另一个exception:

 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,

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

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

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

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

  1. 分离您的顾虑并为不同的界面使用不同的bean
  2. 创建一个扩展OtherInterfacePersonDao的元接口,让bean实现该元接口
  3. 在任何给定时刻将bean转换到您需要的接口。

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