spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本:v5.3.19

文章目录

整合步骤

试想这么一个场景。只用 spring mvc(确切来说是spring-framework), 如何既不搭建web工程(无web.xml)又不用 spring boot 的去整合tomcat部署一个web服务?

1、引入 tomcat 和 spring mvc

xml 复制代码
   		......
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>9.0.62</version>
        </dependency>
    	
    	......

2、实现 WebApplicationInitializer接口,即本篇实现的spring mvc 提供的 AbstractAnnotationConfigDispatcherServletInitializer 类

3、编写controller类

4、编写main函数启动 tomcat

验证:

整个项目目录如下

案例 github 地址:https://github.com/no-shutdown/springmvcandjsp.git

实现原理

ServletContainerInitializer与WebApplicationInitializer

ServletContainerInitializer是Java Servlet 3.0规范中的一部分,接口定义了一个onStartup方法,容器(例如Tomcat、Jetty等)在启动Web应用程序时会调用这个方法。

至于容器如tomcat里的代码是如何得到用户自定义的接口实现类的?

答:通过SPI的方式去获取。

SpringServletContainerInitializer

spring中为我们实现的ServletContainerInitializer实现类如下

@HandlesTypes 是Java Servlet 3.0规范中ServletContainerInitializer接口的一个特性,用于向ServletContainerInitializer提供在类路径上发现的类的信息。比如:这里的意思就是说,会收集路径上所有的 WebApplicationInitializer 类作为方法参数传入。

所以也就是说,在本篇的案例中,main函数启动tomcat时,tomcat会通过SPI的方式去调用到 SpringServletContainerInitializer 类的onStartup方法,而 SpringServletContainerInitializer 又会通过 @HandlesTypes 获取到所有 WebApplicationInitializer 类并调用他们的 onStartup 方法。

所以其实并不一定要继承 AbstractAnnotationConfigDispatcherServletInitializer ,也可以自己去实现一个 WebApplicationInitializer 接口,总之需要在 WebApplicationInitializer#onStratUp 中去启动父子容器。

AbstractAnnotationConfigDispatcherServletInitializer

AbstractAnnotationConfigDispatcherServletInitializer 就是 spring mvc 为我们提供的 WebApplicationInitializer 接口抽象类中的其中一个。其 onStartup 方法做了一些启动父子容器相关的操作。

父容器的启动

registerContextLoaderListener

这个方法做了两件事

1:创建一个spring容器

2:注册 ContextLoaderListener 到servlet容器

创建父容器的代码上图中一起给了,这里再看下 ContextLoaderListener 。

这是一个 ServletContextListener 监听类,在servlet容器启动时,会调用当前servlet容器所注册监听器的 contextInitialized 方法。

而父容器就是在 ContextLoaderListener.contextInitialized 中启动的 ,如下

子容器的启动

registerDispatcherServlet

这个方法做了两件事

1:创建一个spring容器

2:注册 DispatcherServlet 到servlet容器

创建子容器的代码上图中一起给了,这里再看下 DispatcherServlet 。

这个类并不陌生,就是 spring mvc 的前端控制器。

但从servlet容器的角度来看,就是一个普通的HttpServlet,所以必然遵循servlet生命周期。

这个servlet生命周期的 init 方法就会去启动子容器。

相关面试题

1、spring和spring mvc为什么要设计父子容器?

答:单一职责原则,早期spring mvc并不是唯一的主流web框架。为方便快速拔插,比如从 spring mvc 切换到 struts,使用父子容器只需将spring­mvc.xml替换成struts的配置文件struts.xml即可,而spring­core.xml不需要改变。

2、是否可以把所有Bean都交给spring容器(父容器)来管理?

答:不可以,会出现404。因为 spring mvc 在启动时只会扫描当前容器下Controller注册HandlerMethod,并没有同时去查找父容器的bean。

3、是否可以把所有bean都交给spring mvc容器(子容器)来管理?

答:可以 , 因为父容器无非就是包含一些子容器不包含的bean, 如果全在子容器就完全不用父容器了。不过需要注意的是,如果bean都注册到子容器,原本在父容器配置事务、aop等也需要移动到子容器,否则是不会生效的。

相关推荐
霸道流氓气质12 小时前
AI 流式对话与实时聊天平台:SSE 解码竞态、有状态会话隔离与背压取消的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
8Qi813 小时前
DDD架构演进
java·分布式·mvc·ddd
frjc15 小时前
Spring AI 高级实战:ChatMemory、Tool Calling 与 MCP 架构解析
人工智能·spring·架构
T量子永生17 小时前
Unisoc(紫光展锐) Kernel 6.18 DDK 编译框架详解
linux·运维·spring
麻瓜code18 小时前
Spring AI 全模块 API 字典一览(1.1.0 版)
人工智能·windows·spring
养军博客19 小时前
Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离
java·后端·spring·springsecurity
java_nnnn20 小时前
Spring IoC & DI 从入门到理解:控制反转、依赖注入与 Bean 管理详解
数据库·sql·spring
骑着蜗牛撵大象32721 小时前
Spring Boot 4.1 + Spring AI 2.0:RAG 供文、图谱供关系、MCP 供工具的 Java Agent 平台
java·spring boot·spring·微服务·知识图谱·rag·mcp
CoderYanger21 小时前
Java EE 进阶:6.SpringBoot 配置文件(第二弹)
java·spring boot·spring·面试·职场和发展·java-ee·程序员创富
m0_6399069421 小时前
SpringBoot 实现大模型统一调度网关|对接 OpenAI、Claude 协议差异与适配方案
java·spring boot·spring