Spring Error - NoSuchBeanDefinitionException: SessionFactory

advertisements

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);
}


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:

  1. 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

  2. Now, when the container starts, the Root application context is loaded

  3. 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.

  4. 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:

  1. 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.

  2. 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~!

  3. 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:

  1. Add the ContextLoaderListener to your web.xml

  2. 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.

  3. 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.