ContextLoaderListener 和 DispatcherServlet 对比

2,000 阅读3分钟

作者: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的完整关系。

Alt text

  1. ContextLoaderListener 创建父容器。
  2. 每个DispatcherServlet 都会创建一个子容器。
  3. 子容器可以访问父容器中定义的bean。
  4. 父容器中的bean不能直接访问子容器中的bean。
  5. 所有容器都被添加到ServletContext中。
  6. 可以通过WebApplicationContextUtils 类访问父容器。

总结

通常,我们将在DispatcherServlet中定义所有与MVC相关的bean(控制器和视图解析器等),并通过ContextLoaderListener在父容器中定义所有与AOP相关的bean,例如安全,事务等。

通常来说,这样设置是ok的,因为我们很少需要在父容器中安全相关的bean中访问子容器中与MVC 相关的bean。大多数情况下,我们会在MVC的bean中使用安全相关的bean,而这可以通过上面的设置即可做到。