SpringMvc在SpringBoot环境和Web环境中上下文的关系

本文探讨了SpringBoot和传统Web应用中Spring MVC容器管理的区别。在SpringBoot环境下,所有bean由单一容器管理,而在传统Web应用中则存在父(Spring)与子(Spring MVC)容器的结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前有人在我的项目中提出issue,咨询在SpringBoot中的bean是不是由谁来管理的问题(SpringMvc还是Spring)来管理(https://github.com/cmlbeliever/SpringBootLearning/issues/2

其实一开始我也是挺懵逼的,之前没有怎么了解过这些细节,既然提出问题了,当然要找出个所以然。


在Web环境中,是分为SpringMvc管理的子容器,和Spring管理的父容器。父子容器的关系就像类的继承一样,子容器可以获取父容器的bean,反之则是不可以的。

如何验证是两个容器呢?
只要在Controller与Service中实现ApplicationContextAware接口,就可已得知对应的管理容器:
Controller中:
这里写图片描述
Service中:
这里写图片描述

那么在SpringBoot中则是同一个容器管理的(AnnotationConfigEmbeddedWebApplicationContext):
Controller:
这里写图片描述
Service:
这里写图片描述

可以看出在boot环境中使用的是相同的容器管理的,因为AnnotationConfigEmbeddedWebApplicationContext是在包org.springframework.boot.context.embedded中的,那么就可以理解为boot容器管理bean。(当然也可以理解为Spring管理,因为AnnotationConfigEmbeddedWebApplicationContext也只是拓展WebApplicationContext)。


由于SpringMvc的入口都是DispatcherServlet,那是什么原因造成在Web环境中有父子容器,在boot环境中只有一个容器呢?这个当然需要剖析源码咯:

直接debug就可以了,在FrameworkServlet.initWebApplicationContext方法中初始化web上下文环境。

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
//因为调用时Spring容器不是webApplicationContext类型的,所以在webApplicationContext对象时为空
        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        //这里是重点,从这里新建SpringMvc容器
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            onRefresh(wac);
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

当web容器文不存在时,会去创建一个新的web容器:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        //设置父容器,这里的parent即为spring上下文
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());

        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

当web容器初始化成功后,发出ContextRefreshedEvent事件,
DispatcherServlet接收到事件后调用initStrategies方法进行web相关的mapping和adapter设置

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

在SpringBoot环境中,其他流程都是一致的,但是用的容器是AnnotationConfigEmbeddedWebApplicationContext而且还是WebApplicationContext子类,在FrameworkServlet.setApplicationContext时,就对webApplicationContext 进行了赋值

    public void setApplicationContext(ApplicationContext applicationContext) {
        if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
            this.webApplicationContext = (WebApplicationContext) applicationContext;
            this.webApplicationContextInjected = true;
        }
    }

所以在initWebApplicationContext方法初始化的时候用的就是这个全局的上下文,没有新建一个SpringMvc专用的Web上下文
这里写图片描述


总结:
在Web环境中是由Spring和SpringMvc两个容器组成的,在SpringBoot环境中只有一个容器AnnotationConfigEmbeddedWebApplicationContext。也就是可以说是由SpringBoot容器管理的。


SpringBootLearning是对springboot学习与研究项目,是根据实际项目的形式对进行配置与处理,欢迎star与fork。
[oschina 地址]
http://git.oschina.net/cmlbeliever/SpringBootLearning
[github 地址]
https://github.com/cmlbeliever/SpringBootLearning


### Spring框架与Spring MVCSpring Boot关系区别 #### 一、基本概念 Spring是一个全面的企业级应用开发框架,其核心功能包括IoC容器、AOP(面向切面编程)、事务管理等功能[^2]。 Spring MVCSpring框架中的一个重要模块,专门用于支持Web应用程序的开发,遵循MVC设计模式,提供了一个清晰的分层结构来分离业务逻辑、数据展示控制流[^3]。 Spring Boot则是建立在传统Spring框架基础之上的扩展工具集,旨在简化Spring应用的创建、配置部署过程,通过自动配置机制减少繁琐的手动设置工作[^4]。 --- #### 二、关系分析 1. **SpringSpring MVC关系** Spring MVCSpring框架的一部分,继承了Spring的核心特性,如依赖注入(DI)声明式事务管理等。它专门为Web应用场景提供了额外的支持,例如请求映射、视图解析等功能[^2]。 2. **SpringSpring Boot关系** Spring Boot是对Spring框架的功能增强封装,目标是让开发者能够更快速地搭建基于Spring的应用程序。它内置了许多默认配置选项,并通过starter项目简化了依赖管理初始化流程[^5]。 3. **Spring MVCSpring Boot关系** Spring Boot并未取代Spring MVC,而是对其进行了优化支持。当使用Spring Boot开发Web应用时,默认情况下会集成Spring MVC作为底层技术栈,同时利用Spring Boot提供的自动化配置能力进一步降低复杂度[^1]。 --- #### 三、具体区别对比 | 特性 | Spring | Spring MVC | Spring Boot | |---------------------|----------------------------------|---------------------------------|------------------------------| | **定义** | 综合性的企业级应用开发框架 | Spring下的Web开发子模块 | 简化Spring应用构建的工具 | | **适用范围** | 各种类型的Java EE应用 | 主要针对Web应用 | 支持多种类型的应用开发 | | **配置方式** | 手动编写XML或注解 | 基于注解的方式完成大部分配置 | 自动扫描类路径并生成配置文件 | | **启动速度** | 较慢 | 相较纯Spring有所提升但仍需较多步骤 | 显著加快 | | **学习曲线** | 高 | 中等 | 更低 | | **生态系统支持** | 广泛 | 聚焦于HTTP协议相关 | 整合大量第三方库 | 以下是几个关键差异点的具体说明: 1. **配置需求的不同** 使用Spring MVC时,通常需要显式定义Servlet上下文监听器、过滤器以及其他必要的组件;而在Spring Boot环境下,这些都可以依靠约定优于配置的原则自动生成。 2. **依赖管理策略** 在传统的Spring项目里,每一个所需的外部库都需要单独引入并通过版本号严格匹配;相比之下,Spring Boot采用Starter POM的形式打包常用组合,极大地方便了用户的操作。 3. **运行环境适应力** Spring本身并不关心最终产物将以何种形式存在(WAR包或者JAR包),而Spring Boot提倡以嵌入式的Tomcat/Jetty服务器直接打成可执行Jar包发布,从而增强了跨平台移植性灵活性[^3]。 4. **生产效率的影响** 尽管三种技术都能满足一定规模项目的建设要求,但从实际体验来看,借助Spring Boot可以显著缩短迭代周期并提高团队协作效率,因为它屏蔽掉了许多重复劳动环节[^5]。 --- #### 四、代码示例比较 ##### 1. Spring MVC 示例 ```java @Controller public class HelloController { @RequestMapping("/hello") public String sayHello(Model model){ model.addAttribute("message", "Hello from Spring MVC!"); return "index"; } } ``` ##### 2. Spring Boot 示例 ```java @RestController @SpringBootApplication public class HelloWorldApplication { public static void main(String[] args) { SpringApplication.run(HelloWorldApplication.class, args); } @GetMapping("/hello") public String hello() { return "Hello from Spring Boot!"; } } ``` 可以看到,在Spring Boot中不仅省去了复杂的XML配置文件,还通过`@SpringBootApplication`注解实现了全链路的一键启动效果[^4]。 --- ### 总结 综上所述,虽然三个框架都隶属于同一个家族体系下,但它们各自承担着不同的职责定位——Spring负责整体架构支撑,Spring MVC聚焦于网络交互层面的表现形态塑造,最后由Spring Boot将两者有机结合在一起形成一套完整的解决方案集合体[^1]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值