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

20,924

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.

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.

@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 OtherInterface and PersonDao and let your bean implement that metainterface
  3. Cast the bean to to the interface you need at any given moment.
Share:
20,924
smallufo
Author by

smallufo

A Java/Kotlin programmer twitter : http://twitter.com/smallufo

Updated on July 23, 2022

Comments

  • smallufo
    smallufo almost 2 years

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

    This is my AbstractDAO :

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

    And this is my JPA's implementation:

    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 AbstractDao defines simple CRUD methods.

    Interface PersonDao extends AbstractDAO without any addon methods.

    class AbstractDaoJpaImpl defines JPA's implementation of AbstractDao

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

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

    I can use

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

    in my spring's XML file.

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

    @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:

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

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

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

    BUT it throws another exception when initializing Spring :

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

    in AbstractDaoJpaImpl's constructor :

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

    Every question stops here , I cannot find any working solutions 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 ,

  • smallufo
    smallufo over 13 years
    Thanks , I should @Inject OtherInterface otherInterfaceImpl; and test against otherInterfaceImpl. It works !