Spring Autowiring and Class Inheritance

autowiredinheritancespring

I'm having a problem getting @Autowired to work. Sorry if I screw up any terms, I am relatively new to Spring.

Spring Version is 3.0.5.RELEASE, and I am using context:component-scan in my beans definition.

This works with the @Autowired annotation:

@Component
public class UserDao {
    @PersistenceContext
    protected EntityManager em;

    @Transactional
    public User findById(Long id) {
        return em.find(User.class, id);
    }
}

This does NOT work with the @Autowired annotation:

@Component
public class UserDao implements Dao<User> {
    @PersistenceContext
    protected EntityManager em;

    @Transactional
    public User findById(Long id) {
         return em.find(User.class, id);
    }
}

With this setup, all I've added 'implements Dao', and I get a:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [web.rs.persistence.dao.UserDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

And here are some other classes for reference:

Dao.java (interface):

public interface Dao<T extends BaseEntity> {
    T findById(Long id);
}

UserResource.java:

@Component
public class UserResource {
    @Autowired
    UserDao userDao;

    public User getUser(Long id) {
        return userDao.findById(id);
    }
}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

        <context:component-scan base-package="web.rs" />

        <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="classpath:config.properties" />
        </bean>

        <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="loadTimeWeaver">
                <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
            </property>
            <property name="persistenceUnitName" value="${persistence.unit}" />
        </bean>

        <bean id="trx-manager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="emf" />
        </bean>

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

Can anyone shed some light on this issue? I'd love to keep class inheritance.

Thanks!

Best Answer

When using the @Transactional annotation, Spring creates a JDK proxy when the class implements an interface. In your case, the JDK proxy of (UserDao implements Dao<User>) will implement Dao<User> but will not extend UserDao. Therefore, the bean in the context will be a Dao<User>.

When the class with the @Transaction annotation doesn't implement an interface, Spring must create a CGLIB proxy that extends UserDao. Therefore, the bean in the context will be a UserDao.

You can tell Spring to always use CGLIB proxies when putting this in your applicationContext.xml :

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

There are some drawbacks but I don't remember them.

I don't use proxy-target-class="true" and my design is like this :

I have an interface for every type of Dao.

 public interface UserDao extends Dao<User>

   List<User> findByUsername();

I implement the specific interface

 @Component
 public class UserDaoJpa implements UserDao

   public List<User> findByUsername() {
     ...
   }

My service classes uses the UserDao :

 public class UserService {

   @Autowired
   private UserDao userDao;
 }

The bean in the context is a UserDaoJpa and it will be inject where a UserDao is used.