扯谈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中初始化。

时间: 2024-08-29 15:46:54

扯谈spring mvc之WebApplicationContext的继承关系的相关文章

spring MVC 控制器获取WebApplicationContext

使用spring MVC 时,如何在控制器中获取WebApplicationContext 呢? WebApplicationContext  是干什么的? WebApplicationContext 有以下方法:getBean() ,传入bean的id就可以从上下文中获取bean的实例. 使用场景: 一般情况下,我们可以是@Resouce注解,来依赖注入,并不需要手动获取bean的实例再setter. 但是抽象出一个公共的父类Controller,里面使用了泛型T,此时dao的名称我们不知道,

Spring MVC 详解

第一章 Web MVC简介Web MVC简介 1.1.Web开发中的请求-响应模型:   在Web世界里,具体步骤如下: 1.  Web浏览器(如IE)发起请求,如访问http://sishuok.com 2.  Web服务器(如Tomcat)接收请求,处理请求(比如用户新增,则将把用户保存一下),最后产生响应(一般为html). 3.web服务器处理完成后,返回内容给web客户端(一般就是我们的浏览器),客户端对接收的内容进行处理(如web浏览器将会对接收到的html内容进行渲染以展示给客户)

spring mvc

作者:赵磊 博客:http://elf8848.iteye.com   目录 一.前言 二.spring mvc 核心类与接口 三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明 六.springMVC-mvc.xml 配置文件片段讲解 七.spring mvc 如何访问到静态的文件,如jpg,js,css 八.spring mvc 请求如何映射到具体的Action中的方法 九.spring mvc 

Spring MVC 教程,快速入门,深入分析(转载)

作者:赵磊 博客:http://elf8848.iteye.com 下载: Spring的官方下载网址是:http://www.springsource.org/download    (本文使用是的Spring 3.0.5版本) 目录  一.前言二.spring mvc 核心类与接口三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明 六.springMVC-mvc.xml 配置文件片段讲解 七.sp

Spring4.1新特性——Spring MVC增强

Spring 4.1对Spring MVC部分做的增强是最多的,提供了一些视图解析器的mvc标签实现简化配置.提供了GroovyWebApplicationContext用于Groovy web集成.提供了Gson.protobuf的HttpMessageConverter.提供了对groovy-templates模板的支持.JSONP的支持.对Jackson的@JsonView的支持等.   1.GroovyWebApplicationContext  在Spring 4.1之前没有提供Web

开启 Spring &amp;amp; Spring MVC 之旅

不废话了,Spring 有多重要.首先是跑个 Hello World.Spring 最基本功能是 IOC,如果不懂或者好奇原理的可以参考<极简版 Java 依赖注射>. Hello World 怎么获取 Spring?官方推荐 Maven 依赖管理(据说 Ant 也可以),也可以下载 Java 源码构建.但本人比较追求好快多省,不喜欢加一坨坨多余的东西,就想直接下 jar 包.于是找到 http://repo.spring.io/release/org/springframework/. 不知

Spring MVC与JAX-RS比较与分析

导言 过去几年,REST逐渐成为影响Web框架.Web协议与Web应用设计的重要概念. 现在有越来越多的公司希望能以简单而又贴合Web架构本身的方式公开Web API,因此REST变得越来越 重要也就不足为奇了.使用Ajax进行通信的富浏览器端也在朝这个目标不断迈进.这个架构原则提升了万 维网的可伸缩性,无论何种应用都能从该原则中受益无穷. JAX-RS(JSR 311)指的是Java API for RESTful Web Services,Roy Fielding也参与了JAX-RS的制 订

Spring源代码解析(四):Spring MVC

下面我们对Spring MVC框架代码进行分析,对于webApplicationContext的相关分析可 以参见以前的文档,我们这里着重分析Spring Web MVC框架的实现.我们从分析 DispatcherServlet入手: 代码 //这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各 个Spring MVC主要元素的初始化 protected void initFrameworkServlet() throws ServletException, B

Spring MVC向导控制器

概述 假设某一个系统在用户注册模块中需要区别一般用户和高级用户,一般用户只要提供最简单的信息,通过一个小表单就可以搞掂了.但对于需要注册为高级用户的客户来说,论坛希望他们提供详细的注册信息,除了用户名.密码.Email这些最简单的信息外,还需要提供住址.电话以及兴趣爱好之类的调查信息.通过一张大表单让注册者一次性填写所有这些信息并不是一个好主意,大部分潜在的用户当看到这样面目狰狞的"超级表单"后都会毫不犹豫的放弃注册.这时通过一个向导式的表单让用户分步填写注册信息将是明智的方案,虽然需