14

I am stuck with this issue for a long time. I searched for this issue for sometime but none of solution worked.

Structure:

public interface GenericDAO<T extends Serializable, ID extends Serializable>

@Repository
public class AbstractGenericDAO<T extends Serializable, ID extends Serializable> 
    implements GenericDAO<T, ID> {

   private Class<T> persistentClass;

   @Autowired
   private SessionFactory sessionFactory;

   static Logger LOGGER = Logger.getLogger(AbstractGenericDAO.class);


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

   /**
    * @param entity
    * @return T
    * @throws DBException
    */
   @SuppressWarnings("unchecked")
   public T saveEntity(T entity) throws DBException {
       return saveEntity(entity, false);
   }

   /**
    * @param entity
    * @param explicitFlush
    * @return T
    * @throws DBException
    */
   @SuppressWarnings("unchecked")
   public T saveEntity(T entity, boolean explicitFlush) throws DBException {
       Session session = getSessionFactory().getCurrentSession();

       try {
           session.save(entity);
           if(explicitFlush) {
               session.flush();
               session.refresh(entity);
           }
       } catch (HibernateException he) {
           String errorMsg = "Could not save entity. Reason: " + he.getMessage();
           LOGGER.error(errorMsg, he);
           throw new DBException(errorMsg, he);
       }

       return entity;
   }

   /* (non-Javadoc)
    * @see com.amazon.fc.receive.dbaccess.dao.GenericDAO#getPersistentClass()
    */
   @SuppressWarnings("unchecked")
   public Class<T> getPersistentClass() {
       return persistentClass;
   }

   /**
    * @return the sessionFactory
    */
   public SessionFactory getSessionFactory() {
       return this.sessionFactory;
   }

   /**
    * @param sessionFactory the sessionFactory to set
    */
   @Autowired
   public void setSessionFactory(SessionFactory sessionFactory) {
       this.sessionFactory = sessionFactory;
   }
}

public interface ShlkActiveWorkflowDAO 
    extends GenericDAO<ShlkActiveWorkflow, Serializable> 

@Repository
public class ShlkActiveWorkflowDAOImpl 
    extends AbstractGenericDAO<ShlkActiveWorkflow, Serializable>
    implements ShlkActiveWorkflowDAO

I am also using <context:component-scan> in my application-config.xml + <tx:annotation-driven /> in my application-config.xml.

Please provide some information about how to fix this issue.

Exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'abstractGenericDAO' 

Constructor threw exception; nested exception is java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:946)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:890)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:479)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)
    at com.coral.spring.Launcher.<init>(Launcher.java:95)
    at com.coral.spring.Launcher.main(Launcher.java:56)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.workflow.dao.AbstractGenericDAO]: Constructor threw exception; nested exception is     
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:141)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:72)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:938)
    ... 12 more
Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at com.workflow.dao.AbstractGenericDAO.<init>(AbstractGenericDAO.java:43)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
    ... 14 more
Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
user864077
  • 247
  • 2
  • 3
  • 8
  • We need more code from the class **com.workflow.dao.AbstractGenericDAO** – Jean-Philippe Briend Jun 16 '12 at 22:28
  • Do you really need `AbstractGenericDAO` to be annotated with `@Repository`? From your architecture it looks like it only makes sense to inject the more specialized type. Remember that every [Repository](http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/stereotype/Repository.html) is by definition a `@Component` which can be wired. – Anthony Accioly Jun 16 '12 at 22:57
  • I just addded some code for AbstractGenericDAO.java – user864077 Jun 16 '12 at 23:01
  • so should I use @Component instead? – user864077 Jun 16 '12 at 23:04

2 Answers2

18

Remove @Repository annotation from AbstractGenericDAO and make it abstract:

public abstract class AbstractGenericDAO<T extends Serializable, ID extends Serializable> 
   implements GenericDAO<T, ID>

Your problem occurs because @Repository is a specialization of @Component, which means that Spring will try to create AbstractGenericDAO instances for injection. Since AbstractGenericDAO superclass (Object) is not generic, you will not be able to downcast its Type to ParameterizedType, and so this line of code will fail (in the same way that it would if you tried to instantiate it manually with new AbstractGenericDAO()):

this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

The specialized class ShlkActiveWorkflowDAOImpl should still be annotated with @Repository. When spring tries to create a instance of this class a implicit call to AbstractGenericDAO constructor will occur, but this time the line of code mentioned above will run as expected. This happens because getClass() returns ShlkActiveWorkflowDAOImpl.class which is a subclass of the generic AbstractGenericDAO (so the downcast to ParameterizedType works).

Since ShlkActiveWorkflowDAOImpl extends AbstractGenericDAO<ShlkActiveWorkflow, Serializable> the actual type ShlkActiveWorkflow will be correctly reflected at runtime. This is a known workaround to avoid passing a Class<T> reference to the AbstractGenericDAO constructor.

If you are worried about the @Autowired annotation at AbstractGenericDAO, don't be. Spring will correctly wire everything up when you inject a instance of one of its subclasses.

Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
  • Thanks. This working fine but when I added one more annotation inside ShlkActiveWorkflowDAOImpl, then I am getting the same exception. – user864077 Jun 17 '12 at 05:59
  • I have added internal annotation '@Timed' inside one of the methods of ShlkActiveWorkflowDAOImpl – user864077 Jun 17 '12 at 06:00
  • I think you should open a new topic for this error. Anyway, you should not use `@Timed` and other testing annotations in your development layer. Create a separate [testing](http://static.springsource.org/spring/docs/3.0.x/reference/testing.html#unit-testing) layer and inject your object (or a mock). – Anthony Accioly Jun 17 '12 at 14:03
  • I will open a separate topic for this. But '@Timed' is not for testing. This is our annotation for metrics. – user864077 Jun 17 '12 at 16:45
  • I thought that you were referring to [org.springframework.test.annotation.Timed](http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/test/annotation/Timed.html) which is specific for testing. – Anthony Accioly Jun 17 '12 at 16:58
  • But if you are getting the same exception, the problem seems to be that your custom annotation, for some reason, is trying to construct a Instance of `AbstractGenericDAO`... Try creating another component (Ex. ShlkActiveWorkflowMeasures), injecting a instance of `ShlkActiveWorkflowDAOImpl` with @Autowired, and have one of this new component methods annotated with `@Timed`. If it works you can always make that method delegate to the ShlkActiveWorkflowDAOImpl method that you want to measure. – Anthony Accioly Jun 17 '12 at 17:00
  • That worked. I will create a ShlkDataAccessManager and redirect all the DAO calls from there. I can also use '@Timed' annotation there. Thanks. – user864077 Jun 17 '12 at 18:58
2
Type genericSuperClass = getClass().getGenericSuperclass();

ParameterizedType parametrizedType = null;
while (parametrizedType == null) {
   if ((genericSuperClass instanceof ParameterizedType)) {
       parametrizedType = (ParameterizedType) genericSuperClass;
   } else {
       genericSuperClass = ((Class<?>) genericSuperClass).getGenericSuperclass();
   }
}

this.itemClass = (Class<T>) parametrizedType.getActualTypeArguments()[0];