Skip to content

SpringMVC 父子容器是什么知道吗? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

SpringMVC 的父子容器指的是:根容器(Root ApplicationContext,由 ContextLoaderListener 创建)管 Service/DAO 等业务层 Bean,子容器(Servlet ApplicationContext,由 DispatcherServlet 创建)管 Controller、ViewResolver 等 Web 层 Bean。子容器可以访问父容器的 Bean,反之不行——单向依赖,Web 层依赖业务层。

业务架构师视角,我讲两个层面:

分层治理的意图:父容器承载与 Web 无关的业务能力,理论上可被多种接入端复用(Servlet、定时任务、MQ 监听器各自持有子容器,共享同一个根容器)。这本质是**"业务核心与接入渠道分离"**的架构思想在容器层的体现。

现实趋势:Spring Boot 之后这套父子容器基本退场——SpringBootApplication 只创建一个容器,ComponentScan 一次扫完。原因很务实:父子容器带来的经典坑(Service 上 @Transactional 正常、Controller 层 AOP 不生效,因为两个容器各自的配置和代理策略不一致)造成的排查成本,远大于理论上的复用收益。

所以我面试时的标准回答是:讲清机制(历史项目仍在用 XML + WAR 部署时要会维护),然后表明观点——现代业务系统用 Spring Boot 单容器 + 分包/分模块(Maven 多模块)实现分层,边界靠工程结构和依赖检查工具(ArchUnit)约束,而不是靠容器物理隔离。理解历史设计的取舍,比照搬历史设计更重要。

🏗 系统架构师 ​

结构:传统 war 部署的 Spring MVC 有两个容器——Root WebApplicationContext(service/dao 层,ContextLoaderListener 创建)与 Servlet WebApplicationContext(Controller、视图,DispatcherServlet 创建),后者以前者为 parent:子可见父的 Bean,反之不可见。

为什么这样设计:

  • 层隔离:Web 层只能消费业务层,不能反向被依赖,强制单向依赖;
  • 多 DispatcherServlet 共享:多个 Servlet(REST 与页面并存)共享同一套业务 Bean,不重复装配。

现实演进:Spring Boot 默认单一容器——内嵌容器时代双容器的原始动机消失了,简化反而消除了大量心智负担(getRootWebApplicationContext 找不到上下文的经典坑随之消失)。

它留给我的架构遗产是「容器分层隔离」思想:做平台化产品(插件体系、租户定制)时,用父子 ApplicationContext 隔离平台内核与插件——插件之间 Bean 互不可见,只通过父容器暴露的 API 协作,能救回很多灾难性耦合。

💻 全栈开发专家 ​

传统 Spring MVC(web.xml 时代)有两个容器:

  • Root WebApplicationContext:ContextLoaderListener 创建,装 Service、DAO、事务、数据源——业务层容器
  • Servlet WebApplicationContext:每个 DispatcherServlet 一个,装 Controller、视图解析器——Web 层容器,parent 指向 Root

子容器找不到 Bean 会向上委托父容器,反之不行(父不知道子)。设计目的是隔离:可以有多个 DispatcherServlet 各带一套 Controller,共用一套业务层。

真实踩过的坑:事务切面配在了 Servlet 容器的扫描范围、Service 却在 Root 容器,@Transactional 失效;或者两个容器重复扫描同一个包,Bean 创建两遍、单例失效。排查方法:分别打印两个容器的 getBeanDefinitionNames() 对比交集。

Spring Boot 里这个问题消失了:Boot 只启动一个 ApplicationContext,官方明确不再用父子容器,ContextLoaderListener 那套被内嵌容器 + 自动配置取代。所以答完原理我一定补一句"Boot 是单容器",显示真的对比过两者——这也是面试官想听的分水岭。