Reputation: 544
I am quite new to Spring and Java world. I am trying to write a web app looking at petclinic application provided in the samples. However, I am hitting this roadblock since yesterday. It will be great if someone can point me in right direction. I looked up many links on google, but none of the solutions worked for me.
Here is the error root cause
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [org.hibernate.SessionFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:949)
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:818)
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:730)
org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:795)
org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:723)
org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1049)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:490)
. . .
Here is my web.xml in WEB-INF
<display-name>Game's List</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/applicationContext-hibernate.xml</param-value>
</context-param>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<servlet>
<servlet-name>gamelist</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>gamelist</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Here is the applicationContext-hibernate.xml
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- import the dataSource definition -->
<import resource="applicationContext-dataSource.xml"/>
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, Hibernate-related settings for the sessionFactory definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<context:annotation-config />
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource" p:mappingResources="gamelist-hibernate.xml">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
</entry>
</map>
</property>
</bean>
<!-- central data access object: Hibernate implementation -->
<bean id="gameDataStore" class="com.gamelist.datastore.GameItemDataStore"/>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes:
Spring's @Required and @Autowired, as well as JSR 250's @Resource.
-->
<context:annotation-config/>
<!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven/>
<!--
Exporter that exposes the Hibernate statistics service via JMX. Autodetects the
service MBean, using its bean name as JMX object name.
-->
<context:mbean-export/>
This is gamelist-servlet.xml
<context:component-scan base-package="com.gamelist"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
This is the controller class
public class GameLibraryController {
private IDataStore gameDataStore;
@Autowired
public GameLibraryController(GameItemDataStore gameDataStore){
this.gameDataStore = gameDataStore;
}
@RequestMapping("/addGame")
public String addNewGame(Model model){
model.addAttribute("gameItem",new GameItem());
return "library/addgameform";
}
@RequestMapping("/addGameSubmit")
public void add(@ModelAttribute GameItem gameItem,BindingResult result, SessionStatus status){
this.gameDataStore.add(gameItem);
status.setComplete();
}
}
This is the hibernate implementation class GameItemDataStore
@Repository
@Transactional
public class GameItemDataStore implements IDataStore{
private SessionFactory sessionFactory;
@Autowired
public GameItemDataStore(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void add(BaseItem newGame){
sessionFactory.getCurrentSession().save(newGame);
}
}
This is the IDataStore interface
public interface IDataStore {
public void add(BaseItem newGame);
}
Upvotes: 1
Views: 3504
Reputation: 3198
To avoid any confusion, I am going to assume the configuration you mentioned in the question, and start from there. (Ignoring the changes you may have tried after reading the comments on your question)
First a little background:
There are two contexts at play in a Spring MVC application. The applicationContext
which is more accurately referred to as the ROOT WebApplicationContext
, which is meant to be available to all Servlet
level spring contexts, through an integration with the Servlet Container's lifecycle implementation. The second type of context is loaded for each DispatcherServlet
configured in the web.xml
lets call it ServletApplicationContext
Now, when the container starts, the Root application context is loaded
When the Servlet is loaded, the Servlet Application Context is loaded. At this point, beans from the Root Application Context are available to the Servlet Application Context.
IF, you do end up loading the same bean definitions in both the contexts, its like overriding beans in the Servlet Context. Very inefficient to load duplicate beans, and can cause weird errors if you are relying on the same instance being available in all context. But, for simple cases like a single servlet deployment, this should not cause your application to break at startup, spring will deal with this.
Now getting to what's wrong with your configuration:
There is no ContextLoaderListener
configured in your web.xml
. Without a ContextLoaderListener, the ROOT application context is NOT loaded. So none of the beans from /WEB-INF/spring/applicationContext-hibernate.xml
are loaded.
When your servlet kicks up, it loads the beans from gamelist-servlet.xml
, and cannot locate a sessionFactory
bean, because the ROOT Web Application Context was never loaded~!
To confirm this, for a test, comment out all bean definitions from the two context files, just keep empty <beans></beans>
tags in them. Start your server, you'll see a log statement at the end that says something like no ROOT Web Application Context found, configure ContextLoaderListener in web.xml
And the fix:
Add the ContextLoaderListener
to your web.xml
You may choose to continue your configuration as you have it now, or follow the advice given in earlier comments and include only MVC related definitions in the servlet context and all else in the ROOT application context.
Another, simpler option would be to simply have an empty application context xml for the ROOT Web Application Context, and configure all beans in the servlet context. This is not correct from a purist
design point of view, but a practical simple solution, provided you plan on having a single servlet application.
Sorry for the looong description, but I felt some explanation was mandated. Hope this helps.
Upvotes: 6
Reputation: 280177
Get rid of
<bean id="gameDataStore" class="com.gamelist.datastore.GameItemDataStore"/>
which you won't need because of @Repository
on your class and
<context:annotation-config />
which you have twice. Then add
<context:component-scan base-package="com.your.package" />
where com.your.package
is a top level package that contains the GameItemDataStore
class.
Upvotes: 1