Search code examples
springspring-mvcautowiredmanaged-beanapplicationcontext

How can I make WebApplicationContext access beans in ApplicationContext?


This is how I try to bootstrap Spring in my Java Web Application:

public class SpringBootstrap implements WebApplicationInitializer {
    public void onStartup(ServletContext servletContext) throws ServletException {
        final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
        rootContext.register(RootContextConfiguration.class);

        final AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.register(WebContextConfiguration.class);
        webContext.setParent(rootContext);

        final DispatcherServlet dispatcherServlet = new DispatcherServlet(webContext);
        final ServletRegistration.Dynamic dispatcher = servletContext.addServlet("springDispatcher", dispatcherServlet);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");

        final ContextLoaderListener contextLoadListener = new ContextLoaderListener(webContext);
        servletContext.addListener(contextLoadListener);
    }
}

And RootContextConfiguration and WebContextConfiguration classes are as follows:

@Configuration
@ComponentScan(basePackages = "biz.tugay.janeleven.root")
public class RootContextConfiguration {
}

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "biz.tugay.janeleven.web")
public class WebContextConfiguration {
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        return resolver;
    }
}

When I try deploying the application to Tomcat and start I will get:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [biz.tugay.janeleven.root.service.AppUserService] 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)}

I am also including the HelloServlet and AppUserService classes here, with their package information:

package biz.tugay.janeleven.root.service;

@Service
public class AppUserService {

    public AppUser addAppUser(String name) {
        final AppUser appUser = new AppUser(name);
        AppUserDB.addUser(appUser);
        return appUser;
    }

    public Collection<AppUser> getAllAppUsers() {
        return AppUserDB.getAll();
    }
}

package biz.tugay.janeleven.web;

@Controller
@RequestMapping(value = "/")
public class HelloWorldServlet {

    @Autowired
    private AppUserService appUserService;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public String helloWorld(Model model) {
        final Collection<AppUser> appUserList = appUserService.getAllAppUsers();
        model.addAttribute("appUserList", appUserList);
        return "hello.jsp";
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String addApplicationUser(@RequestParam("username") String username) {
        appUserService.addAppUser(username);
        return "redirect:/";
    }

}

My expectation is that, WebContextConfiguration should be able to find beans that are managed by RootContextConfiguration since I am calling:

webContext.setParent(rootContext);

during the bootstrap process. But obviously I am missing something.


Solution

  • You are correct that the web context will inherit from the root context, however you did not include the rootContext when you initialized servletContext.addListener(); see similar code below which will help you fix your issue.

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
    
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(AppConfig.class);
        servletContext.addListener(new ContextLoaderListener(rootContext));
    
        AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebConfig.class);
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher",
                new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    
    }