在Spring MVC中我们需要提供DispatcherServlet的实现,通常可以由扩展AbstractAnnotationConfigDispatcherServletInitializer类并实现getRootConfigClasses、getServletConfigClasses和getServletMappings三个方法完成。其中getRootConfigClasses指定了RootConfig来创建Root WebApplicationContext,getServletConfigClasses指定了WebConfig来创建Servlet WebApplicationContext。
In the Web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. The root WebApplicationContext should contain all the infrastructure beans that should be shared between your other contexts and Servlet instances. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given Servlet instance.
由官方文档知道,Root WebApplicationContext中包含了供多个Servlet WebApplicationContext使用的Bean,比如Services、Repositories等;而Servlet WebApplicationContext中包含的是Controllers、ViewResolvers等。当Spring MVC程序在Servlet WebApplicationContext中找不到要使用的Bean时会向Root WebApplicationContext进行求助。
因此对于WebConfig的配置应写为:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"xxx.controller"})
public class WebConfig implements WebMvcConfigurer {
// TODO
}
对于RootConfig的配置应写为:
@Configuration
@ComponentScan(basePackages = {"xxx"}, excludeFilters = {@Filter(type = FilterType.ANNOTATION, value = {EnableWebMvc.class, Controller.class})})
public class RootConfig {
// TODO
}
这样可以避免某个Bean同时出现在Root WebApplicationContext和Servlet WebApplicationContext中。那么我们该如何查看在这两种WebApplicationContext中的Bean呢?可以通过注入的方式:
@RestController
@RequestMapping({"/"})
public class ListController {
@Autowired
private ServletContext servletContext;
@RequestMapping(value = "/rootlist", method = GET)
public List<String> rootList() {
ApplicationContext applicationContext = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
return Arrays.asList(applicationContext.getBeanDefinitionNames());
}
@Autowired
private WebApplicationContext webApplicationContext;
@RequestMapping(value = "/weblist", method = GET)
public List<String> webList() {
return Arrays.asList(webApplicationContext.getBeanDefinitionNames());
}
}