之前的博客搭建spring mvc应用的时候,我们使用spring中的ContextLoaderListener和DispatcherServlet分别加载了不同的spring配置文件。spring mvc程序里会有两个WebApplicationContext,一个是parent,从applicationContext.xml里加载的,一个是child,从servlet-context.xml里加载的。 两者是继承关系,child WebApplicationContext 可以通过getParent()函数获取到root WebApplicationContext。
还是之前的代码和配置,我们修改下FirstController.java来验证下父子关系:
package net.aty.springmvc;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import net.aty.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.DispatcherServlet;
@Controller
public class FirstController {
@Autowired
private MyService service;
@Autowired
private HttpServletRequest request;
public FirstController() {
System.out.println("I am a controller.");
}
@RequestMapping("/mvc/first/hello.do")
@ResponseBody
public String hello(@RequestParam("userName") String userName) {
testContext();
return service.process(userName);
}
private void testContext() {
WebApplicationContext root = ContextLoader
.getCurrentWebApplicationContext();
ServletContext servletContext = root.getServletContext();
// true
System.out
.println(root == servletContext
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
// true
System.out.println(root == WebApplicationContextUtils
.getWebApplicationContext(servletContext));
WebApplicationContext child = (WebApplicationContext) request
.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
// false
System.out.println("root:" + root.containsLocalBean("myService"));
// true
System.out
.println("root:" + root.containsLocalBean("firstController"));
// false
System.out.println("child:" + child.containsLocalBean("myService"));
// true
System.out.println("child:"
+ child.containsLocalBean("firstController"));
// true
System.out.println("is parent==" + (child.getParent() == root));
}
}
当我们再浏览器访问http://localhost:8080/SpringMvcDemo/mvc/first/hello.do?userName=11的时候,后台输出:
代码有几点需要说明一下:
1.我们可以通过ContextLoader.getCurrentWebApplicationContext()获取rootWebApplicationContext。
2.也可以通过WebApplicationContextUtils.getWebApplicationContext(servletContext)获取root。
3.可以通过request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE)获取child。
4.使用containsLocalBean()可以检查自己的是否包含这个bean,不会从父context中查找这个bean。
区分不同的spring context也会带来一些问题,摘自这篇文章。
1.不少开发者不知道Spring mvc里分有两个WebApplicationContext,导致各种重复构造bean,各种bean无法注入的问题。
2.有一些bean,比如全局的aop处理的类,如果先root WebApplicationContext里初始化了,那么child
WebApplicationContext里的初始化的bean就没有处理到。如果在child WebApplicationContext里初始化,
在root WebApplicationContext里的类就没有办法注入了。
3.区分哪些bean放在root/child很麻烦,不小心容易搞错,而且费心思。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
如果确实需要2个spring配置文件,root WebApplicationContext是通过listener初始化的,child WebApplicationContext是通过servlet初始化的。
而在applicationContext.xml里通常只component-scan非Controller的类,如:
<context:component-scan base-package="io.github.test">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<context:component-scan base-package="io.github.test.web" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
参考文章: 扯谈spring mvc之WebApplicationContext的继承关系