Java 抽象 DAO 模式和 Spring 的“Proxy cannot be cast to ...”问题!

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3852564/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 05:51:09  来源:igfitidea点击:

Abstract DAO pattern and Spring's "Proxy cannot be cast to ..." problem!

javaspringcglibgenericdao

提问by smallufo

I know this is very often asked , but I cannot find a working solution :

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

This is my AbstractDAO :

这是我的 AbstractDAO :

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

And this is my JPA's implementation:

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

And this is one entity's dao :

这是一个实体的道:

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

Here is its implementation:

这是它的实现:

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

The whole architecture is simple :

整个架构很简单:

Interface AbstractDaodefines simple CRUD methods.

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

Interface PersonDaoextends AbstractDAO without any addon methods.

接口PersonDao扩展了 AbstractDAO 而没有任何附加方法。

class AbstractDaoJpaImpldefines JPA's implementation of AbstractDao

AbstractDaoJpaImpl类定义了 JPA 对 AbstractDao 的实现

class PersonDaoImplextends AbstractDaoJpaImpl and implements PersonDao AND OtherInterface , which adds aditionalMethods()...

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

IF , PersonDaoImpl only implements PersonDao , without implementing OtherInterface.additionalMethods() , everything works fine.

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

I can use

我可以用

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

in my spring's XML file.

在我的 spring 的 XML 文件中。

BUT , PersonDaoImpl implements OtherInterface(s) , when testing/running , I have to cast the DAO from PersonDao to PersonDaoImpl or OtherInterfaces, such as :

但是, 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(...));
  }
}

The problem occurs when (PersonDaoImpl) dao, which throws "Proxy cannot be cast to PersonDaoImpl" exception:

问题发生在(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)

this is often asked when googleing , everyone suggest adding proxy-target-class="true"to <tx:annotation-driven>:

这是经常有人问googleing时,大家建议增加proxy-target-class="true"<tx:annotation-driven>

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

This will make use of CGLIB instead of JDK's dynamic proxy.

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

BUT it throws another exception when initializing Spring :

但是它在初始化 Spring 时抛出另一个异常:

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

in AbstractDaoJpaImpl's constructor :

在 AbstractDaoJpaImpl 的构造函数中:

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

Every question stops here , I cannot find any workingsolutions now.

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

Can anyone give me a working solution ? Thanks a lot !

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

Environment : Spring-3.0.4 , javaee-api-6.0 , javax.inject , cglib-2.2 , hibernate-jpa-2.0-api-1.0.0 ,

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

采纳答案by Sean Patrick Floyd

You're solving the wrong problem. Proxied beans are not meant to be casted to the original classes, one way or the other. That would break the whole point of dependency injection. After all: when you specify a dependency as an interface you are requesting a bean that fulfills a contract, but not the implementation details. Casting it to the original bean class breaks this loose coupling.

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

You are saying the additional methods are backed up by an interface you call OtherInterface, so why not use that instead? After all, the proxy will implement all the target class' interfaces, not only the injected one.

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

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

Basically you have these options (sorted from clean to dirty):

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

  1. Separate your concerns and use different beans for different interfaces
  2. Create a meta-interface that extends OtherInterfaceand PersonDaoand let your bean implement that metainterface
  3. Cast the bean to to the interface you need at any given moment.
  1. 分离你的关注点,为不同的接口使用不同的 bean
  2. 创建延伸的metainterface OtherInterfacePersonDao让你的bean实现一个metainterface
  3. 在任何给定时刻将 bean 投射到您需要的接口。

回答by srikanth N

yes spring always creates proxy classes and thats how it actually discovered non intrusive weaving and aop by xml config... try googling for that error in spring documentation there shud be rules to follow and work arounds.

是的,spring 总是创建代理类,这就是它如何通过 xml 配置实际发现非侵入性编织和 aop...