Search code examples
javaspringcomponent-scan

component-scan not finding @Repository


My classes look like this: AbstractDAO

package dao.impl;
public abstract class AbstractDAO <E> implements DAO<E> {

@PersistenceContext
private EntityManager em;

   public void add(E entity) {
        em.persist(entity);
   }
}

DAOImpl

package dao.impl;
@Transactional
@Repository
public class ItemDAOImpl extends AbstractDAO<Item> {

}

application-context-test.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" xmlns:mvc="http://www.springframework.org/schema/mvc"
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.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

<context:component-scan base-package="dao.impl" />

<bean id="entityManagerFactoryBean"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />

    <property name="packagesToScan" value="domain" />

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.archive.autodetection">class</prop>
            <prop key="hibernate.hbm2ddl.auto">create</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        </props>
    </property>
</bean>

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/inventory" />
    <property name="username" value="root" />
    <property name="password" value="1234" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>

<tx:annotation-driven />

TEST class

package service.impl;

@ContextConfiguration(locations = "classpath:application-context-test.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class ItemTest {

@Autowired
private ItemDAOImpl itemDAO;

@Test
public void testCreateItem() throws Exception {
    Item item = new Item("cellphone", "galaxy", ItemType.TECHNOLOGY, 10000);
    itemDAO.add(item);        
    assertEquals(5, itemDAO.list(Item.class).size());
}
}

Should not this code be able to autowire my itemDAO?

When I run my test it throws an exception saying

org.springframework.beans.factory.BeanCreationException: Could not autowire 
field: private dao.impl.ItemDAOImpl service.impl.ItemTest.itemDAO; nested
exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [dao.impl.ItemDAOImpl] found for dependency: 
expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations: 

Could you please tell what am I missing? The only thing I can think about is that since my test is in src/test/java, my application-context-test.xml is in src/test/resources and my dao is in src/main/java. Maybe the component-scan scanning in the wrong place?


Solution

  • Because ItemDAOImpl is annotated with @Transactional, spring will create a proxy for this bean and inject the proxy, not the bean itself when autowiring.

    Spring can create proxies by subclassing (using Cglib) or by implementing the beans interfaces with Jdk proxies. Which type of proxy spring uses, depends on your configuration.

    I had similar problems as you described and the cause was, that spring used Jdk proxies and I wasnt aware of it.

    In your case the spring bean for ItemDaoImpl would be a proxy implementing DAO. This cannot be injected into

    @Autowired
    private ItemDAOImpl itemDAO;
    

    because it cannot be cast to ItemDaoImpl. That would explain the exception you are facing.

    To fix that, change the field to

    @Autowired
    private DAO<Item> itemDAO;
    

    The above only works, if you are using spring 4.

    With earlier versions of spring you have to create an interface

    public interface ItemDAO extends DAO<Item>
    

    and let ItemDaoImpl implement it. Finally change your field where you want it to be injected to

    @Autowired
    private ItemDAO itemDAO;