ContextLoaderListener对比DispatcherServlet

发表于:2018-08-22 20:56:57,已有776次阅读

原文:https://howtodoinjava.com/spring-mvc/contextloaderlistener-vs-dispatcherservlet/
作者:Lokesh Gupta,于2018年5月10日。

在基于XML配置的Spring MVC中,你肯定看到了在web.xml文件声明的这两项:ContextLoaderListenerDispatcherServlet。接下来就来让我们看一下他们在框架中的作用及差异。

根上下文与子上下文

在阅读之前,必须理解如下内容:

DispatcherServlet - 子应用上下文

DispatcherServlet本质上就是一个Servlet(它继承自HttpServlet),它的主要作用就是拦截传入的Request请求与配置的URL规则进行匹配,它接受传入的URI并找到正确的Controller与视图View。它是前端控制器。
当你在Spring配置中定义DispatcherServlet时,你通过contextConfigLocation属性,指定了一个包含有控制器Controller类,View映射等定义的XML文件,web.xml

<servlet>
    <servlet-name>employee-services</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:employee-services-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

如果你不配置指定一个xml文件,DispatcherServlet将默认使用[servlet_name]-servlet.xml。Web应用可以定义任意数量的DispatcherServlet项,每个servlet都将在自己的命名空间中运行,加载属于它自己的应用上下文(Application Context),其中包含mapping映射与hanlder处理器等。
这意味着每个DispatcherServlet都可以访问Web应用程序上下文。在指定之前,每个DispatcherServlet都会创建自己的内部Web应用程序上下文

从Spring 3.x开始,使用方法DispatcherServlet(WebApplicationContext webApplicationContext)给创建的DispatcherServlet指定Web应用程序上下文。它只能在Servlet 3.x环境中通过ServletContext.addServlet(java.lang.String,java.lang.String)API得到支持。

ContextLoaderListener – 根应用上下文

ContextLoaderListener创建一个根应用上下文, 它可以被所有DispatcherServlet创建的子应用上下文所共享。web.xml配置中,只能有一个这样配置项:

<listener>
  <listener-class>
    org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>
  
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>

ContextLoaderListener中包含的beans是全局可见的。像Service,DAO, 基础Bean等。当根上下文被创建后,他将会作为属性保存在ServletContext中,如下这样(org/springframework/web/context/ContextLoader.java):

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

//属性名定义在/org/springframework/web/context/WebApplicationContext.java中

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

在Spring的Controller中可以通过WebApplicationContextUtils类来访问根上下文:

@Autowired
ServletContext context;

ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(context);

if(ac == null){
    return "root application context is null";
}    

ContextLoaderListener与DispatcherServlet

下图描述了单一视图中两者的关系:
ContextLoaderListener vs DispatcherServlet

  1. ContextLoaderListener创建根应用上下文。
  2. DispatcherServlet为每个servlet创建一个子应用上下文。
  3. 子应用上下文可以访问根应用上下文中定义的Bean。
  4. 根应用上下文中的Bean无法直接的访问子上下文中的Bean。
  5. 所有的应用上下文都会被添加到ServletContext中。
  6. 你可以使用WebApplicationContextUtils类访问根上下文。

总结

一般来说, 你会在DispatcherServlet的应用上下文中定义与MVC有关的Bean,如控制器Controller与视图Bean。而对应的与安全,事务,Service等有关的Bean会被定义在ContextLoaderListener的上下文中。
通常,这种配置工作正常,因为你很少需要访问任何MVC Bean(从子上下文)到安全相关的类(从根上下文)中。大多数情况下,我们会使用上面的配置,在MVC类上使用安全有关的Bean。

学习愉快!!

这里就有一个在开发过程中遇到的坑,就是在DispatcherServlet定义的子上下文中不要使用<context:component-scan base-package="根包名" />的行式偷懒扫描使用注解声明的所有Bean,这样及容易与ContextLoaderListener中的根上下文定义声明的Bean起冲突,比如会造成根上下文中定义的切面AOP事务配置失效。还是要像文中所说的那样在根上下文中定义或扫描数据库操作DAO,Service,事务等基础Bean。在子上下文中定义或扫描与MVC有关的Bean(如拦截器,控制器等)。

分享到:

扫一扫,用手机观看!

用微信扫描还可以

分享至好友和朋友圈

二维码分享
评论

暂无评论

您还可输入120个字