Announcement Announcement Module
Collapse
No announcement yet.
ContextLoaderListener and DispatcherServlet Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ContextLoaderListener and DispatcherServlet

    Hi, newbie to Spring and the forum so first post! Ok, I've been reading up and working in my spare time with the Spring modules and just a little confused over the configuration, specifically how ContextLoaderListener and DispatcherServlet load beans and how to reference beans loaded through the ContextLoaderListener from the controllers like below in the web.xml.

    Code:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
            
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        
        <context-param>
            <param-name>contextConfigLocation</param-name>		
    	<param-value>/WEB-INF/conf/services.xml</param-value>  
    	<param-value>/WEB-INF/conf/hibernate-dao.xml</param-value>        
        </context-param>
        
        <servlet>
            <servlet-name>bank</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>bank</servlet-name>
            <url-pattern>*.htm</url-pattern>
        </servlet-mapping>
        
    </web-app>
    So far I've a fairly basic application broken up into controller, service, and dao layers. I've defined a Controller bean in my bank-servlet.xml which is loaded through the DispatcherServlet like so:

    bank-servlet.xml

    Code:
    <?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:p="http://www.springframework.org/schema/p"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <bean id="viewResolver" 
    	class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    	p:prefix="/WEB-INF/jsp/" p:suffix=".jsp">
    </bean>
    	<bean id="accountController"	
    class="com.bank.web.controller.AccountController"/>
    </beans>
    Originally I had a reference to the service class here but read that you shouldn't have any middle tier components defined here, besides as the services.xml references the dao components I'd end up having to define all of them within the bank-servlet.xml making additional configuration files redundant.

    Originally posted by robh View Post
    DispatcherServlet will always load its own configuration file using <servlet_name>-servlet.xml. It is intended that this file will contain web components such as Controllers, ViewResolvers and LocaleResolvers - but no middle tier components.

    The ContextLoaderListener is then used to load the files containing your middle tier and data tier components. Spring will merge all these components into an ApplicationContext making your middle tier components accessible from your web tier components.

    Rob
    http://forum.springsource.org/showthread.php?t=18953


    In my services.xml I still have references to the hibernate-dao.xml like below:

    services.xml
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean name="accountService" class="com.bank.service.AccountServiceImpl">
        <property name="accountDAO" ref="accountDAO" />
    </bean>
    
    <bean name="accountDAO" class="com.bank.domain.dao.spring.AccountHiberDAOImpl">
    
    </bean> 
    
    </beans>
    and in the hibernate-dao.xml references to the Session factory, transactionManager etc like below:

    hibernate-dao.xml

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
            <property name="url" value="jdbc:db2:bank" />
            <property name="username" value="db2admin" />
            <property name="password" value="damobamo" />
        </bean>
    <!-- Hibernate SessionFactory -->
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="mappingResources">
                <list>
                    <value>com/bank/domain/model/Account.hbm.xml</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
                    <prop key="hibernate.hbm2ddl.auto">create</prop>
                    <prop key="hibernate.jdbc.use_get_generated_keys">false</prop>
                </props>
            </property>
        </bean>
    <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" autowire="byType">
            <!-- property name="sessionFactory" ref="sessionFactory" /-->
        </bean>
        <bean id="accountDAO" class="com.bank.domain.dao.spring.AccountHiberDAOImpl" autowire="byType">
            <!-- property name="sessionFactory" ref="sessionFactory" /-->
        </bean>
    </beans>
    I'm a little confused in how to wire it together, specifically how to reference the service layer from the controllers.

    What I want to be able to do now is access the service layer beans from within the controller. However I'm not sure how to access the beans that should have already been instantiated by the container based on the config files specified under contextConfigLocation.

    I've debugged into the controller to see if there's any references to the service beans. All I could find is references to the beans declared in hibernate-dao.xml in my Controller from the applicationContext.parent attribute. i.e.
    Code:
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
    			HttpServletResponse response) throws Exception {
    ...
    AccountHiberDAOImpl accountDAO = (AccountHiberDAOImpl) getApplicationContext().getParent().getBean("accountDAO");
    ...}
    This works fine but bypasses the service layer entirely making it redundant.

    I can't seem to find any reference to service layer beans. The only way I've found to ensure I get a reference to this is to remove the contextConfigLocation reference from the web.xml and import all other config files within the bank-servlet.xml. I'm not sure if that would be considered the right approach plus rather than just use it because it works I'd like to figure out what's wrong with what I'm doing. I just can't figure out why my services.xml is not being included in the applicationContext.getParent(). Also I'm not sure if this would be the ideal approach to accessing middle tier layers from the Controller layer or is there a more standard approach to this?

    To ensure the services.xml is being loaded correctly through the ContextLoaderListener I commented out <param-value>/WEB-INF/conf/hibernate-dao.xml</param-value> and get the following error :

    Error creating bean with name 'accountDAO' defined in ServletContext resource [/WEB-INF/conf/services.xml]

    I just can't find any reference to the service layer at runtime through the applicationContext on the controller.

    Any advice, useful links, best practises tips on how to wire presentation and middle tier together would be really appreciated. Also apologies for the length of this post!

    Thanks!

  • #2
    typically enough it's only after posting that I finally figured out what was wrong. Looks like I choose a bad example to set up my contextConfigLocation in my web.xml where the second configuration file was always overwriting the first.

    It should have been
    Code:
    <context-param>
            <param-name>contextConfigLocation</param-name>		
    	 <param-value>/WEB-INF/conf/services.xml /WEB-INF/conf/hibernate-dao.xml</param-value>  		      
        </context-param>
    instead of
    Code:
     <context-param>
            <param-name>contextConfigLocation</param-name>		
    	<param-value>/WEB-INF/conf/services.xml</param-value>  
    	<param-value>/WEB-INF/conf/hibernate-dao.xml</param-value>        
      </context-param>
    doh! can't believe I spent half a day trying to figure this out! Though think writing the whole thing down gave me a new perspective on it (apologies to any who read through the whole thing!).

    However part of my question still applies. Would accessing the service layer through the parent context from the Controller be considered an acceptable approach?

    i.e

    Code:
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
    			HttpServletResponse response) throws Exception {
    		
    		AccountService accountService = (AccountService)  
                      getApplicationContext().getParent().getBean("accountService");
    		Account account = accountService.createAccount();
    where Account is a domain pojo?

    Thanks

    Comment


    • #3
      However part of my question still applies. Would accessing the service layer through the parent context from the Controller be considered an acceptable approach?
      No it isn't. Simply inject it into your controller.

      The DispatcherServlet (or the ApplicationContext loaded in there) has access to all beans loaded by the ContextLoaderListener. So simply inject it instead of doing the ugly lookup...

      Comment


      • #4
        thanks for the reply Marten. That makes a bit more sense, thought the other way seemed a bit convoluted, not to mention inelegant. So instead of getting the parent all I have to do is :
        Code:
        AccountService accountService = (AccountService)getApplicationContext().getBean("accountService");
        Didn't realise the ApplicationContext from DispatcherServlet had an implicit reference to beans loaded through the ContextLoaderListener. Been reading so many different docs I think I missed that.

        thanks for the tip!

        Comment


        • #5
          That is again not the BEST way to do it. You should INJECT your dependencies.

          Code:
          public class AccountController {
          
            private AccountService accountService;
          
            public void setAccountService(AccountService accountService) {
              this.accountService=accountService;
            }
          }
          then in your xml

          Code:
          <bean class="AccountController" >
            <property name="accountService" ref="accountService" />
          </bean>
          In general when you are doing a lookup think again there is probably a better way.

          Comment


          • #6
            Ok thats what I thought you meant by inject rather than a lookup, just wasn't 100% sure when you mentioned the Dispatcher having access to ContextLoaderListener loaded beans.

            What you've outlined below is what I originally did and that's where the confusion lies. From reading comments like below which I mentioned in my first mail I removed the Service from the xml and the attribute from the controller.

            Originally posted by DamoKen View Post
            Originally I had a reference to the service class here but read that you shouldn't have any middle tier components defined here, besides as the services.xml references the dao components I'd end up having to define all of them within the bank-servlet.xml making additional configuration files redundant.


            Originally posted by robh View Post
            Originally Posted by robh
            DispatcherServlet will always load its own configuration file using <servlet_name>-servlet.xml. It is intended that this file will contain web components such as Controllers, ViewResolvers and LocaleResolvers - but no middle tier components.

            The ContextLoaderListener is then used to load the files containing your middle tier and data tier components. Spring will merge all these components into an ApplicationContext making your middle tier components accessible from your web tier components.

            Rob
            http://forum.springsource.org/showthread.php?t=18953
            The reason I originally questioned this approach was I found that if I referenced the Service component in the bank-servlet.xml I then also needed to reference the DAO as this will be injected into the Service component. If I don't also include a reference to this I will get a null pointer exception when the injected Service component attempts to access the DAO.

            I've inspected the context in the controller trying this approach and there are now two Services available, one accessible through getApplicationContext().getParent().getBean("Servi ceName") and the other that has been injected.

            The component retrieved from the Parent has a properly initialised DAO object injected as expected due to the ref in the service.xml whereas the Service injected into the controller doesn't.

            So the reason I was questioning this approach was as I have to define both the service component as well as the dao component in the bank-servlet.xml in order for the Service to initialise correctly, what is the point of having a seperate service.xml?

            Also as I am accessing an injected instance of the service defined in the bank-servlet, what is the point of loading the service through the ContextLoaderListener? How exactly should beans loaded through this be then accessed?

            thanks for your help!

            Comment


            • #7
              Your rant totally lost me. What are you doing? Your servlet should only load the *-servlet.xml file. That shoudl only contain the web stuff and references to beans loaded in the ContextLoaderListener. You don't need to load the xml AGAIN or reference it with an import because that will give you 2 instances of the beans.

              ContextLoaderListener -> Services, daos and infrastructure
              DispatcherServlet -> web stuff no xml that contains something of the above

              The dispatcherservlets applicationcontext automatically has the ContextLoaderListeners applicationcontext as a parent. So don't define the beans or import xml that is loaded by the contextloaderlistener.

              Comment


              • #8
                apologies Marten, that wasn't meant as a rant, simply a question as I was just a bit confused.

                I wasn't loading the xml again, or importing. The mistake I made was I assumed that by placing a ref to the service within the *-servlet.xml I would also need to define the bean, i.e.
                Code:
                <bean id="accountController"
                		class="com.bank.web.controller.AccountController"
                		p:accountService-ref="accountService"/>
                	
                <bean id="accountService"
                		class="com.bank.service.AccountServiceImpl"
                		p:accountService-ref="accountDAO" />
                hence two instances. I understand what you mean now plus the implications when you said dispatcherservlets applicationcontext automatically has the ContextLoaderListeners applicationcontext as a parent. As long as the referenced bean is defined within the config files loaded into ContextLoaderListeners applicationcontext with the same id as that given in the ref, the Container will automatically be able to inject it. It all makes sense now.

                Up until now all the tutorials I read always had a bean definition within the same file as any reference to it, I hadn't come across any examples with multiple files hence my mistake. Thanks for clearing that up and in the future I'll try to be a little clearer.

                Comment


                • #9
                  hi there. I am using ContextLoaderListener with DispatcherServlet, i m having problem

                  hey all.

                  Thing is that i made my sample project (java web project) and added Spring capabilities and hibernate capabilities. In my web.xml i defined ContextLoaderListener and gave ContextConfigLocation
                  ************************************************** *********
                  <listener>
                  <listener-class>
                  org.springframework.web.context.ContextLoaderListe ner
                  </listener-class>

                  </listener>
                  <context-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>
                  /WEB-INF/applicationContext.xml
                  /WEB-INF/applicationContext-hibernate.xml


                  </param-value>

                  </context-param>

                  ************************************************** *******
                  applicationContext.xml is containing beans (conrollers, managers and DAO declarations)
                  applicationContext-hibernate.xml is containing dataSource, sessionFactory and transactionManager information.


                  after that i decided to add MVC module of spring and add following into web.xml

                  ************************************************** **
                  <servlet>
                  <servlet-name>action</servlet-name>
                  <servlet-class>
                  org.springframework.web.servlet.DispatcherServlet
                  </servlet-class>
                  <load-on-startup>2</load-on-startup>
                  </servlet>

                  <servlet-mapping>
                  <servlet-name>action</servlet-name>
                  <url-pattern>*.htm</url-pattern>
                  </servlet-mapping>

                  ************************************************** *****
                  I have added action-servlet.xml in WEB-INF folder. and provided "viewResolver" and "urlMapping". BUt when i try to run the project following error occur

                  **************************************************
                  javax.servlet.ServletException: No adapter for handler [controller.CityController@17ebdf8]: Does your handler implement a supported interface like Controller?


                  ************************************************** *
                  Although I have provided CityController in action-servlet.xml

                  ************************************************** *
                  This is being a headache for me since one day. Is this not the way to use Spring + Spring MVC + Hibernate

                  **********************************
                  I m using forums for the very first time. Sorry if i forget any discipline.

                  Comment


                  • #10
                    hey

                    hey is anyone there to help me out?

                    Comment

                    Working...
                    X