扯谈spring mvc之WebApplicationContext的继承关系

简介:

spring mvc里的root/child WebApplicationContext的继承关系

在传统的spring mvc程序里会有两个WebApplicationContext,一个是parent,从applicationContext.xml里加载的,一个是child,从servlet-context.xml里加载的。
两者是继承关系,child WebApplicationContext 可以通过getParent()函数获取到root WebApplicationContext。

简单地说child WebApplicationContext里的bean可以注入root WebApplicationContext里的bean,而parent WebApplicationContext的bean则不能注入child WebApplicationContext里的bean。

一个典型的web.xml的内容是:

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/applicationContext.xml</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

其中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>

servlet-context.xml里通常只component-scan Controller类,如:

    <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>

如果不这样子分别component-scan的话,可能会出现Bean重复初始化的问题。

上面是Spring官方开始时推荐的做法。

root/child WebApplicationContext继承关系带来的麻烦

root WebApplicationContext里的bean可以在不同的child WebApplicationContext里共享,而不同的child WebApplicationContext里的bean区不干扰,这个本来是个很好的设计。

但是实际上有会不少的问题:
* 不少开发者不知道Spring mvc里分有两个WebApplicationContext,导致各种重复构造bean,各种bean无法注入的问题。
* 有一些bean,比如全局的aop处理的类,如果先root WebApplicationContext里初始化了,那么child WebApplicationContext里的初始化的bean就没有处理到。如果在child WebApplicationContext里初始化,在root WebApplicationContext里的类就没有办法注入了。
* 区分哪些bean放在root/child很麻烦,不小心容易搞错,而且费心思。

一劳永逸的解决办法:bean都由root WebApplicationContext加载

在一次配置metrics-spring时,对配置@EnableMetrics配置在哪个WebApplicationContext里,感到很蛋疼。最终决定试下把所有的bean,包括Controller都移到root WebApplicationContext,即applicationContext.xml里加载,而servlet-context.xml里基本是空的。结果发现程序运行完全没问题。

后面在网上搜索了下,发现有一些相关的讨论:

http://forum.spring.io/forum/spring-projects/container/89149-servlet-context-vs-application-context

spring boot里的做法

在spring boot里默认情况下不需要component-scan的配置,于是猜测在Spring boot里是不是只有一个WebApplicationContext?

后面测试下了,发现在spring boot里默认情况下的确是只有一个WebApplicationContext:org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext,所以在spring boot里省事了很多。

总结

spring 的ApplicationContext继承机制是一个很好的设计,在很多其它地方都可以看到类似的思路,比如Java的class loader。但是在大部分spring web程序里,实际上只要一个WebApplicationContext就够了。如果分开rott/child WebApplicationContext会导致混乱,而没什么用。

所以推荐把所有的Service/Controller都移到root WebApplicationContext中初始化。

目录
相关文章
|
自然语言处理 前端开发
一文学会text-justify,orientation,combine文本属性
一文学会text-justify,orientation,combine文本属性 在深度剖析text-align家族和你不知道的下划线-text-decoration两篇介绍文本属性的时候,我们基本已经学会了很多之前没有使用过的属性,今天我们接着来看更多的文本属性,CSS的世界是精妙的,无尽的,仅仅希望同这三篇文章,可以入得CSS文本属性的基础门。人生短暂,学无止尽。
641 156
|
边缘计算 运维 监控
阿里云全站加速DCDN重磅升级
相比传统CDN加速,全站加速DCDN具有更广阔的应用场景。在当下企业全面数字化的进程中,为了更全面地满足广大企业客户的个性化加速需求,全站加速DCDN从简单开通到个性化定制、从内容分发到安全防护,对客户侧的使用体验进行了全局梳理和全链路优化,全站加速产品功能得到极大增强和完善。12月1日14:00,全站加速DCDN集中升级发布,四位产品专家在线解读了边缘程序、数据日志、边缘安全背后的技术与应用。
1682 155
阿里云全站加速DCDN重磅升级
|
缓存 安全 搜索推荐
直播预告 | 全站加速DCDN重磅升级发布会
全站加速DCDN重磅升级发布会
651 154
直播预告 | 全站加速DCDN重磅升级发布会
|
边缘计算 Cloud Native 算法
阿里云 卢日:GRTN打造阿里云边缘云最佳流媒体场景实践
在9月16日上午召开的分布式云主题报告会上,阿里云资深技术专家卢日发表了题为《GRTN打造阿里云边缘云最佳流媒体场景实践》的精彩演讲。
1242 152
阿里云 卢日:GRTN打造阿里云边缘云最佳流媒体场景实践
|
数据挖掘 双11 数据格式
第一批吃螃蟹的人,真香!
双11的一个大项目。
3082 155
第一批吃螃蟹的人,真香!
|
人工智能 机器人
阿里云RPA为财险企业提效900%
财险企业不免存在大量信息录入,保单打印等工作,因此不少企业陷于客户信息确认并录入、车辆信息录入以及险种信息录入和保单打印等繁杂单一的低附加值日常事务性工作中无法抽身,进而无法开展新的工作。
3392 153
阿里云RPA为财险企业提效900%
|
安全 云栖大会 数据安全/隐私保护
云栖大会演讲回顾 | 云市场心选包销伙伴赖炳辉:包销心选,大有可为
导语:云市场心选包销伙伴赖炳辉为大家介绍网站建设的现状与市场分析,以及自己携手阿里云云市场的收获、心得。
2386 155
云栖大会演讲回顾 | 云市场心选包销伙伴赖炳辉:包销心选,大有可为