作者:Lokesh Gupta
原文出处:howtodoinjava.com/spring-mvc/…
在使用web.xml配置Spring MVC时,我们必定看过两种声明:ContextLoaderListener和DispatcherServlet。让我们试着理解它们在框架中的目的以及不同之处。
子父容器
在继续深入之前,请理解如下两点:
- 可以同时存在多个不同的Spring容器,其中一个为根容器,其余的为子容器。
- 所有子容器均可访问根容器中定义的bean,但是父容器不能访问子容器中的bean。
DispatcherServlet —— 子容器
DispatcherServlet本质上是一个Servlet(继承了HttpServlet),它被用于处理与预设的URL形式相匹配的的web请求。DispatcherServlet会为传入的URI找到合适的控制器和视图的组合。所以DispatcherServlet是一个前端控制器。
当使用spring的XML配置文件来定义一个Dispatcherservlet时,要在配置文件中设置控制器、视图解析器等,供Dispatcherservlet的contextConfigLocation属性使用。(译者注:contextConfigLocation是DispatcherServlet的父类FrameworkServlet的属性)
web.xml
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>springMVC.xml</param-value>
</context-param>
每个DispatcherServlet均可以访问web应用的上下文。除非经过指定,每个DispatcherServlet都会创建自己内部的spring容器,用于持有spring mvc相关的bean。
注意:从spring 3.x开始,DispatcherServlet(WebApplicationContext webApplicationContext)将会使用给定的spring MVC容器创建对象。这一点要依赖servlet 3.x的ServletContext.addServlet(java.lang.String, java.lang.String) 方法。
ContextLoaderListener – 父容器
ContextLoaderListener创建父容器,并且父容器是所有由DisapatcherServlet创建的子容器共享的。一个web.xml文件中只能配置一个父容器。
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
父容器中包含了全局可用的bean,例如service,repository等。父容器在被创建之后,会存储到ContextLoader类的ServletContext属性中:
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
可以使用WebApplicationContextUtils 类来获取控制器的父容器。
Controller.java
@Autowired
ServletContext context;
ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(context);
if(ac == null){
return "root application context is null";
}
ContextLoaderListener vs DispatcherServlet
下图描述了单个DispatcherServlet与ContextLoaderListener的完整关系。
- ContextLoaderListener 创建父容器。
- 每个DispatcherServlet 都会创建一个子容器。
- 子容器可以访问父容器中定义的bean。
- 父容器中的bean不能直接访问子容器中的bean。
- 所有容器都被添加到ServletContext中。
- 可以通过WebApplicationContextUtils 类访问父容器。
总结
通常,我们将在DispatcherServlet中定义所有与MVC相关的bean(控制器和视图解析器等),并通过ContextLoaderListener在父容器中定义所有与AOP相关的bean,例如安全,事务等。
通常来说,这样设置是ok的,因为我们很少需要在父容器中安全相关的bean中访问子容器中与MVC 相关的bean。大多数情况下,我们会在MVC的bean中使用安全相关的bean,而这可以通过上面的设置即可做到。