Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Repository autowiring fails

I'm working on this problem for quite some time, and can't get it to work. Hope some of you could spare your time to help me.

The scenario
I'm building a web application which uses Spring MVC. This webapp ist supposed to use another Maven project, which contains the persistence layer. The persistence layer currently only consists of a service and a Spring Data repository. The persistence layer itself works against a local MySQL instance.

My Problem
I have a JUnit test for the persistence layer. The test loads and saves an entity. This is handled over the service. Both operations work fine.

Now i'm trying the same within a JUnit test within the Maven project of the webapp. Again i'm trying to save and load an entity. But this time it fails. Spring states that the Repository reference inside the service couldn't be autowired, cause there is no bean that would fit.

I have tried multiple approaches to solve this. For example using direct wiring, giving qualifiers and so one. None of my approaches worked. I even build two complete new Maven projects where I simplified the scenario, but left the necessary elements the same. There it worked like a charm. I don't understand what i'm doing wrong on this one, since I adapted the settings from the working example.


Files from the persistence project

persistence.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:jpa="http://www.springframework.org/schema/data/jpa"
       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
          http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" default-autowire="byName">

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

    <context:component-scan base-package="com.company.dev.webapp" />
    <jpa:repositories base-package="com.company.dev.webapp.repository" query-lookup-strategy="create-if-not-found" />

    <bean id="exceptionPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

    <!-- Data Source -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost/webapp" />
        <property name="username" value="root" />
        <property name="password" value="" />
        <property name="initialSize" value="20" />
    </bean>

    <!-- Hibernate -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="webapp-persistence" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <property name="database" value="MYSQL" />
            </bean>
        </property>
    </bean>

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



QuestionService

@NoRepositoryBean
public interface QuestionService extends QuestionRepository {
   Question findById(Long id);

   Question findByUniqueRef(String uniqueRef);

   List<Question> findAll(Sort sort, Page page);

   List<Question> findAll(Page page);
}



QuestionServiceImpl

@Transactional
@Service
public class QuestionServiceImpl implements QuestionService {

    @Autowired
    QuestionRepository questionRepository;

    @Override
    public Question save(Question entity) {
        return questionRepository.save(entity);
    }
    [Some methods ommited]
}



QuestionRepository

@Transactional
@Repository
public interface QuestionRepository extends JpaRepository<Question, Long>{

}


Files from the webapp project

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <!-- Spring Bind Macro -->
    <context-param> 
        <param-name>defaultHtmlEscape</param-name> 
        <param-value>true</param-value> 
    </context-param> 

  <!-- Log4J -->
    <context-param> 
        <param-name>log4jConfigLocation</param-name> 
        <param-value>/WEB-INF/log4j.xml</param-value> 
    </context-param> 
    <context-param> 
        <param-name>log4jRefreshInterval</param-name> 
        <param-value>15000</param-value> <!-- intervall in millisekunden!!! --> 
    </context-param> 
    <context-param> 
        <description>Zur Konfiguration von Log4j</description> 
        <param-name>webAppRootKey</param-name> 
    <!-- Diesen Wert bei jeder Anwendung anpassen! -->
        <param-value>app.root</param-value> 
    </context-param> 

    <!-- Application Context -->    
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:persistence-context.xml
            /WEB-INF/spring/app-config.xml
        </param-value>
    </context-param>


  <!-- Reads request input using UTF-8 encoding -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  <!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  <!-- Log4j Konfiguration laden-->
    <listener> 
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> 
    </listener>

  <!-- Handles all requests into the application -->
    <servlet>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/spring/mvc-config.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>oemInViewFilter</filter-name>
        <filter-class>
            org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
        </filter-class>
        <init-param>
            <param-name>entityManagerFactoryBeanName</param-name>
            <param-value>entityManagerFactory</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>oemInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

app-config.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"
       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
        ">

  <!-- Application Message Bundle -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/messages/messages"/>
        <property name="cacheSeconds" value="0"/>
    </bean>

  <!-- Services and Repositories -->
    <bean class="com.company.dev.webapp.impl.SimpleQuestionRepository" id="questionRepository">
    <!-- ... -->
    </bean>

  <!-- Events -->
    <bean class="com.company.dev.webapp.event.impl.ApplicationContextEventGateway" id="eventGateway"/>

  <!-- Search and indexing -->
    <import resource="search-indexing.xml"/>

</beans>

mvc-config.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"
       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
        ">

  <!-- Application Message Bundle -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/messages/messages"/>
        <property name="cacheSeconds" value="0"/>
    </bean>

  <!-- Services and Repositories -->
    <bean class="com.company.dev.webapp.impl.SimpleQuestionRepository" id="questionRepository">
    <!-- ... -->
    </bean>

  <!-- Events -->
    <bean class="com.company.dev.webapp.event.impl.ApplicationContextEventGateway" id="eventGateway"/>    
</beans>

JUnit test

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration
@ContextConfiguration({"classpath:persistence-context.xml", "classpath:app-config.xml"})
@Transactional
public class QuestionServiceTest {

    @Autowired
    private QuestionService questServ;
    private Question quest;

    @Before
    public void setUp() throws Exception {
        if (quest == null) {
            quest = new Question();
            quest.setQuestion("Why?!");
            quest.setText("Please help!");
            quest.setCreationDate(new Date(System.currentTimeMillis()));
            questServ.save(quest);
        }
    }

    @Test
    public void loadQuestion() {
        Question check = questServ.findOne(quest.getId());
        Assert.assertNotNull(check);
        Assert.assertEquals(check, quest);
    }
}


The Exception

08:28:59 ERROR [test.context.TestContextManager]: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@15a3a1] to prepare test instance [com.company.dev.webapp.service.QuestionServiceTest@a9a32c]
java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:308)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:333)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:220)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:301)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:303)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:518)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1052)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:906)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'answerController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.company.dev.webapp.service.QuestionService com.company.dev.webapp.web.AnswerController.questionRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'questionServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.dev.webapp.repository.QuestionRepository com.company.dev.webapp.service.impl.QuestionServiceImpl.questionRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.dev.webapp.repository.QuestionRepository] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:574)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:280)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:304)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.company.dev.webapp.service.QuestionService com.company.dev.webapp.web.AnswerController.questionRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'questionServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.dev.webapp.repository.QuestionRepository com.company.dev.webapp.service.impl.QuestionServiceImpl.questionRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.dev.webapp.repository.QuestionRepository] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:507)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
    ... 38 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'questionServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.dev.webapp.repository.QuestionRepository com.company.dev.webapp.service.impl.QuestionServiceImpl.questionRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.dev.webapp.repository.QuestionRepository] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:838)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:780)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:697)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
    ... 40 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.company.dev.webapp.repository.QuestionRepository com.company.dev.webapp.service.impl.QuestionServiceImpl.questionRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.dev.webapp.repository.QuestionRepository] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:507)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
    ... 51 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.dev.webapp.repository.QuestionRepository] 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)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:914)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:783)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:697)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
    ... 53 more



This is the whole code. If anything else is needed, please let me now. I appreciate help on this topic, since I'm really clueless how to solve this.

Best Regards

like image 991
Makenshi Avatar asked Mar 01 '12 07:03

Makenshi


People also ask

Why can't I auto-wire a repository to a spring application?

Make sure the @Service or @Component that is trying to auto-wire the repository isn't in the same directory as your SpringApplication.class. Make sure it's in a subfolder like service/. Sometimes I had the same issues when I forget to add Lombok annotation processor dependency to the maven configuration

Is @AutoWired spring specific?

Read more here. @Autowired is Spring specific. The others are not. You can for example name a @Component like: There are several reasons @Autowired might not work.

What happens if you don't annotate a class in Spring Boot?

Basically, Spring is going to scan the project's classpath (or paths you specified), find all of the @Component classes and make them available for auto-wiring. So if you forget to annotate a class it will not be auto-wired and when you try and use it you will get a null and a NullPointerException.

What are some alternatives to @AutoWired?

This sets a default class to be wired. Some other alternatives are to use @Resource, @Qualifier or @Inject. Read more here. @Autowired is Spring specific.


1 Answers

I found the solution. After writing an custom implementation of the QuestionRepository and wiring it directly through xml, a new Exception showed up which solves the mystery. The bean declaration

<!-- Services and Repositories -->
    <bean class="com.company.dev.company.impl.SimpleQuestionRepository" id="questionRepository">
    <!-- ... -->
    </bean>

was meant to provide a bean of a simple self written Repository which only persists the objects in a collection in memory.

Since it's id is questionRepository Spring tries to autowire by name it on the questionRepository field in the class QuestionServiceImpl.

Long story, short fact: Double check the names which someone else may have defined on your configuration files. :)

like image 67
Makenshi Avatar answered Sep 30 '22 09:09

Makenshi