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
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
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.
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.
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.
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. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With