Spring 中ContextLoaderListener和DispatcherServlet所加载的context的关系

本文解析了Spring框架中ContextLoaderListener与DispatcherServlet加载applicationContext的区别及关联。前者负责加载全局上下文,后者加载特定于Spring MVC的上下文。两者间存在父子关系,允许全局上下文中的bean被Spring MVC上下文中的bean引用。
摘要由CSDN通过智能技术生成

区别

  • ContextLoaderListener加载的applicationContext是web应用全局的上下文,而DispatcherServlet加载的applicationContext是spring MVC的上下文
  • ContextLoaderListener所加载的context被spring通过servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context)存放到ServletContext的attribute中。该上下文可通过WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)或WebApplicationContextUtils.getWebApplicationContext(servletContext)方法来获取。
    DispatcherServlet加载context完成后,如果publishContext属性的值设置为true的话(缺省为true) 会将context存放在ServletContext的key为org.springframework.web.servlet.FrameworkServlet.CONTEXT. + (servletName)的attribute中。

    xml配置如下:

    <servlet>  
        <servlet-name>dispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath*:dispatcher-servlet.xml</param-value>  
        </init-param>  
    </servlet>  
  • DispatcherServlet所加载的applicationContext可以认为是mvc私有的context,由于保存在servletContext中的key值与通过ContextLoaderListener加载进来的applicationContext使用的key值不相同,因此如果只使用DispatcherServlet加载context的话,如果程序中有地方使用WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext) 来试图获取applicationContext时,就会抛出”No WebApplicationContext found: no ContextLoaderListener registered?”的exception。

关联

  • Spring的ContextLoaderListener所创建出来的context和Spring MVC DispatcherServlet所创建出来的context是父子关系,FrameworkServlet在实例化对应的applicationContext后通过setParent将从ServletContext中获取到的ContextLoaderListener创建的applicaitonContext设置成父上下文,然后加载在对应的xml配置文件对其初始化。
  • father WebApplicationContext里的bean可以被注入到child WebApplicationContext里的bean,而child WebApplicationContext的bean则不能被注入到parent WebApplicationContext里的bean。所以在使用Spring MVC时启用自动检测功能,应在applicationContext.xml里只component-scan非Controller的类,而在Spring MVC里只component-scan Controller类

    applicationContext.xml配置如下

    <context:component-scan base-package="com.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>

    dispatcher-servlet.xml的配置如下

    <context:component-scan base-package="com.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>

    如果不这么分开扫描的话,那么父容器和子容器中都会有相对应的bean实例。他们因为不在同一个容器中,所以虽然他们bean的id相同也不报错,但这无疑会加应用的负担。同时会造成难以发现的问题。比如我遇到的一个问题:

    将一个ApplicationListener实现加上@Component注解,在applicationContext.xml和dispatcherServlet.xml中配置了相同的扫描方案。在触发监听事件的时候调用两次onApplicationEvent方法。这是因为在Spring的上下文和Spring MVC的上下文有两个相同名称的bean,在调用完子类中的监听器后Spring回去查看父容器中是否也有监听器在监听对应的事件。如果有则调用监听器方法

注意

虽然这两个context上下文是一对父子关系,但它们加载的bean不是合并存储的,所以个人建议,基于mvc相关的spring配置由DispatcherServlet加载,而其余的JavaBean都交给ContextLoaderListener加载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值