Announcement Announcement Module
Collapse
No announcement yet.
Bean from root-context not available in servlet-context? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Bean from root-context not available in servlet-context?

    Hi, I am trying to figure out why I cannot access a bean from the root-context.xml in the servlet-context.xml. I have a very simple spring mvc app (using spring 3.1.1.RELEASE) and am running into an issue where a field inside a controller annotated with

    @Autowired
    private TaskServiceImpl taskService;

    throws
    org.springframework.beans.factory.NoSuchBeanDefini tionException

    Checking the log for the servlet-context.xml, where the homeController resides gives:

    Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@1e6cecc:
    defining beans [
    org.springframework.web.servlet.mvc.method.annotat ion.RequestMappingHandlerMapping#0,
    org.springframework.format.support.FormattingConve rsionServiceFactoryBean#0,
    org.springframework.validation.beanvalidation.Loca lValidatorFactoryBean#0,
    org.springframework.web.servlet.mvc.method.annotat ion.RequestMappingHandlerAdapter#0,
    org.springframework.web.servlet.handler.MappedInte rceptor#0,
    org.springframework.web.servlet.mvc.method.annotat ion.ExceptionHandlerExceptionResolver#0,
    org.springframework.web.servlet.mvc.annotation.Res ponseStatusExceptionResolver#0,
    org.springframework.web.servlet.mvc.support.Defaul tHandlerExceptionResolver#0,
    org.springframework.web.servlet.handler.BeanNameUr lHandlerMapping,
    org.springframework.web.servlet.mvc.HttpRequestHan dlerAdapter,
    org.springframework.web.servlet.mvc.SimpleControll erHandlerAdapter,
    org.springframework.web.servlet.resource.ResourceH ttpRequestHandler#0,
    org.springframework.web.servlet.handler.SimpleUrlH andlerMapping#0,
    org.springframework.web.servlet.view.InternalResou rceViewResolver#0,
    homeController,
    org.springframework.context.annotation.internalCon figurationAnnotationProcessor,
    org.springframework.context.annotation.internalAut owiredAnnotationProcessor,
    org.springframework.context.annotation.internalReq uiredAnnotationProcessor,
    org.springframework.context.annotation.internalCom monAnnotationProcessor,
    org.springframework.context.annotation.internalPer sistenceAnnotationProcessor,
    org.springframework.context.annotation.Configurati onClassPostProcessor$ImportAwareBeanPostProcessor# 0];
    parent: org.springframework.beans.factory.support.DefaultL istableBeanFactory@166de66

    And the log for the parent root-context.xml:

    Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@166de66:
    defining beans [
    dataSource,
    transactionManager,
    org.springframework.aop.config.internalAutoProxyCr eator,
    org.springframework.transaction.annotation.Annotat ionTransactionAttributeSource#0,
    org.springframework.transaction.interceptor.Transa ctionInterceptor#0,
    org.springframework.transaction.config.internalTra nsactionAdvisor,
    emf,
    taskService]; root of factory hierarchy

    As can be seen, the root context contains the taskService bean that is needed for autowiring and it is correctly configured. I have checked this by moving the taskService to the servlet-context.xml, and everything works as expected in that case.

    My question is can I inject beans into controllers from the root-context? I thought I could do so, but based on this example it seems that it does not work. Can anybody help me figure out what I may be doing wrong? I am actually more interested in the reasoning behind it than finding an alternative, as I have mentioned this issue goes away if i move the bean in the servlet-context.xml.

  • #2
    Program to interfaces not concrete classes. Judging by the name you should autowire TaskService and not TaskServiceImpl...

    Comment


    • #3
      Thank you Marten, I agree with you that I made a rookie (which I am ) mistake in using the implementation class, but I was not aware that a context can only inherit beans from a parent context by interface, and not the actual defined class.

      I wrote some logging for the context after implementing ApplicationContextAware:
      log.info("Beans of type TaskServiceImpl: " + Arrays.toString(context.getBeanNamesForType(TaskSe rviceImpl.class)));
      log.info("Beans of type TaskService: " + Arrays.toString(context.getBeanNamesForType(TaskSe rvice.class)));
      ApplicationContext parent = context.getParent();
      log.info("Beans of type TaskServiceImpl: " + Arrays.toString(parent.getBeanNamesForType(TaskSer viceImpl.class)));
      log.info("Beans of type TaskService: " + Arrays.toString(parent.getBeanNamesForType(TaskSer vice.class)));


      Then i added the bean in both contexts:
      <bean id="taskService" class="com.company.project.service.impl.TaskServic eImpl"/>

      and I got:
      Context: WebApplicationContext for namespace 'appServlet-servlet'
      Beans of type TaskServiceImpl: [taskService]
      Beans of type TaskService: [taskService]
      Context: Root WebApplicationContext
      Beans of type TaskServiceImpl: []
      Beans of type TaskService: [taskService]

      Does anyone know what is the reasoning behind this result?

      Comment


      • #4

        Thank you Marten, I agree with you that I made a rookie (which I am ) mistake in using the implementation class, but I was not aware that a context can only inherit beans from a parent context by interface, and not the actual defined class.
        Well it depends on your setup. You probably have some transaction management configured which creates a dymanic proxy of your class. It acts like an implementation of the TaskService and adds the behavior and hence it isn't a TaskServiceImpl anymore. You could use class based instead of interface based proxies which would make it work for implementations.


        Does anyone know what is the reasoning behind this result?
        See above and read the aop chapter in the reference guide.


        Then i added the bean in both contexts:
        Now you have 2 instances of the bean 1 with transactions and one without. Add the same configuration from the root context to your servlet context and try again and you will have the same exception. It isn't about parent contexts it is about the fact that there is a proxy created due to transactions (or whatever triggers proxy creation).
        Last edited by Marten Deinum; Jun 16th, 2012, 03:20 AM.

        Comment


        • #5
          Thank you again Marten, that is very clear

          I have double checked the documentation on AOP and if my bean implements an interface, interface proxying is used by default. In the rare case where I would need to use a method not on a interface or need to pass the specific implementation class, I can force CGLIB proxying as explained in the manual:

          http://static.springsource.org/sprin...xy-force-CGLIB

          I did not have this need, but I added it here for anyone else just starting with Spring that may have missed this point in the manual.

          Comment

          Working...
          X