深入理解 Spring Web 应用程序初始化流程

前言

在构建基于 Spring 的 Web 应用程序时,了解初始化流程是至关重要的。本文将详细介绍 Servlet 容器的初始化过程,并重点探讨 Spring 框架在其中的作用,特别是 ServletContainerInitializerSpringServletContainerInitializerWebApplicationInitializer 这些关键机制。

一、Servlet 容器初始化过程

在理解 Spring 框架在 Web 应用程序初始化中的作用之前,首先需要了解 Servlet 容器的初始化过程。Servlet 容器负责加载和初始化 Web 应用程序中的 ServletFilterListener,并在应用程序启动时执行一系列初始化操作。

  1. Servlet 容器启动:当启动 Web 应用程序时,Servlet容器(如 Tomcat、Jetty 等)会启动并开始初始化。
  2. 扫描部署描述符:Servlet 容器会扫描部署描述符(web.xml 文件),了解 Web 应用程序中的Servlet、Filter 和 Listener 配置。
  3. 初始化 Servlet:Servlet 容器根据 web.xml 中的配置,初始化 Servlet,并调用其 init() 方法。
  4. 初始化 Filter:类似地,Servlet 容器也会初始化在 web.xml 中配置的 Filter,并调用其 init() 方法。
  5. 初始化 Listener:容器会初始化在 web.xml 中配置的 Listener,并调用其相应的初始化方法。
  6. 执行 ServletContext 监听器:容器还会触发 ServletContext 监听器的初始化方法,这些监听器通常由 Servlet 容器提供,用于处理全局的 ServletContext 级别的初始化逻辑。
  7. 应用程序启动完成:一旦所有 Servlet、Filter 和 Listener 都被初始化,并且 ServletContext 监听器也执行完毕,Web 应用程序就完成了启动过程。

二、ServletContainerInitializer 接口

ServletContainerInitializer 是 Servlet 3.0 规范中引入的一个接口,它允许框架和库在 Servlet 容器启动时动态注册 Servlet、Filter、Listener 等组件,而无需在 web.xml 中进行显式的配置。

Servlet 容器在启动时会扫描类路径上所有的 JAR 文件和类,查找实现了 ServletContainerInitializer 接口的类,并调用其 onStartup 方法。开发者可以在这个方法中进行一些初始化工作,比如动态注册 Servlet、Filter、Listener 等组件,设置 Servlet 容器的上下文参数等。

public interface ServletContainerInitializer {void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

如下所示,我们创建了一个名为 MyServletContainerInitializer 的类,并实现了 ServletContainerInitializer 接口。ServletContainerInitializer 接口通过 @HandlesTypes 注解指定 Servlet 容器要扫描的类。(当 Servlet 容器启动时,会扫描类路径下所有的类,并将实现了 @HandlesTypes 注解中指定类型的类传递给对应的 ServletContainerInitializer 实现类)在 onStartup 方法中,我们遍历了所有实现了 MyServlet 接口的类,并将其实例化后添加到 ServletContext 中。

// 使用 @HandlesTypes 注解指定需要处理的类,即实现了 MyServlet 接口的类
@HandlesTypes(MyServlet.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {@Override// Servlet 容器启动时会调用 onStartup 方法public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException {if (c != null) {// 如果传入的类集合不为空for (Class<?> clazz : c) {// 遍历传入的类集合if (MyServlet.class.isAssignableFrom(clazz)) {// 如果类是 MyServlet 接口的实现类try {// 尝试实例化该类MyServlet servlet = (MyServlet) clazz.newInstance();// 创建该类的实例// 将 Servlet 实例注册到 ServletContextservletContext.addServlet("MyServlet", servlet);// 并映射到指定路径servletContext.getServletRegistration("MyServlet").addMapping("/myservlet");} catch (InstantiationException | IllegalAccessException e) {// 捕获实例化异常// 抛出 ServletExceptionthrow new ServletException(e);}}}}}
}

三、SpringServletContainerInitializer

Spring 框架为 Servlet 容器初始化过程提供了强大的支持。SpringServletContainerInitializer 类实现了 ServletContainerInitializer 接口,用于在 Servlet 容器启动时触发 Spring 应用程序上下文的初始化。这使得 Spring 能够以自动化的方式完成配置和初始化。

// 使用 @HandlesTypes 注解指定需要处理的类,即实现了 WebApplicationInitializer 接口的类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {// 当 Servlet 容器启动时调用的方法@Overridepublic void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {// 创建一个空的 WebApplicationInitializer 列表List<WebApplicationInitializer> initializers = Collections.emptyList();// 如果传入的 webAppInitializerClasses 不为空if (webAppInitializerClasses != null) {// 创建一个新的 ArrayList 以存储初始化器initializers = new ArrayList<>(webAppInitializerClasses.size());// 遍历传入的类集合for (Class<?> waiClass : webAppInitializerClasses) {// 确保类不是接口、不是抽象类,并且实现了 WebApplicationInitializer 接口if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&WebApplicationInitializer.class.isAssignableFrom(waiClass)) {// 使用反射实例化类,并将其添加到初始化器列表中try {initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass).newInstance());} catch (Throwable ex) {// 如果实例化失败,则抛出 ServletExceptionthrow new ServletException("Failed to instantiate WebApplicationInitializer class", ex);}}}}// 如果初始化器列表为空if (initializers.isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");// 记录日志,表示未检测到任何 Spring WebApplicationInitializer 类return;// 返回,结束方法}// 记录日志,表示检测到了多少个 Spring WebApplicationInitializer 类servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");// 对初始化器列表进行排序AnnotationAwareOrderComparator.sort(initializers);// 遍历初始化器列表,调用每个初始化器的 onStartup 方法,传入 ServletContextfor (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}}
}

四、WebApplicationInitializer 接口

WebApplicationInitializer 接口是 Spring 提供的另一个重要机制,用于配置和初始化 Spring Web 应用程序。通过实现这个接口,开发者可以以编程方式配置 Servlet、Filter、Listener 等组件,并设置它们的初始化参数、映射等信息。

public interface WebApplicationInitializer {void onStartup(ServletContext servletContext) throws ServletException;}

以下是一个简单的示例,展示了如何在 Spring MVC 中使用 WebApplicationInitializer

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class<?>[] { RootConfig.class };}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class<?>[] { ServletConfig.class };}@Overrideprotected String[] getServletMappings() {return new String[] { "/**" };}
}

在这个示例中,我们创建了一个名为 MyWebAppInitializer 的类,并继承了 AbstractAnnotationConfigDispatcherServletInitializer

AbstractAnnotationConfigDispatcherServletInitializer.png

这个抽象类是 WebApplicationInitializer 接口的便捷实现,它简化了配置 Spring MVC 应用程序的步骤。

我们需要实现三个方法:

  1. getRootConfigClasses() 方法:用于指定 Spring 根上下文的配置类。在这里,我们可以配置 Spring 应用程序中的服务层、数据访问层等。
  2. getServletConfigClasses() 方法:用于指定 Spring MVC 的配置类。在这里,我们可以配置 Spring MVC 相关的内容,如控制器、视图解析器等。
  3. getServletMappings() 方法:用于指定 DispatcherServlet 的映射路径。在这里,我们可以配置 DispatcherServlet 监听的请求路径。

以上的简单示例,展示了如何在 Spring MVC 中使用 WebApplicationInitializer 进行应用程序的初始化配置。通过这种方式,我们可以以编程方式配置 Spring MVC 应用程序,而不需要使用传统的 web.xml 文件。

五、小结

在构建基于 Spring 的 Web 应用程序时,深入了解 Servlet 容器的初始化流程及 Spring 框架在其中的作用是至关重要的。通过了解 ServletContainerInitializer、SpringServletContainerInitializer 和 WebApplicationInitializer 等关键机制,开发者可以更好地理解和掌握应用程序的初始化过程,从而更加灵活地管理和配置自己的应用程序。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/841349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

源码部署ELK

目录 资源列表 基础环境 关闭防护墙 关闭内核安全机制 修改主机名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 单节点 集群(3台节点集群为例) 启动 二、部署logstash 部署logstash 添加配置文件 启动 三、部署kiban…

数据清洗操作及众所周知【数据分析】

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 前面的博客 数据分析—技术栈和开发环境搭建 …

Vue3实战笔记(45)—VUE3封装一些echarts常用的组件,附源码

文章目录 前言一、柱状图框选二、折线图堆叠总结 前言 日前使用hooks的方式封装组件&#xff0c;在我使用复杂的图标时候遇到了些问题&#xff0c;预想在onMounted中初始化echarts&#xff0c;在使用hooks的时候&#xff0c;组件没有渲染完&#xff0c;使用实例会出现各种各样…

Qt Creator(1)【概述篇】

阅读导航 引言一、Qt概述1. 什么是Qt2. Qt的发展史3. Qt支持的平台4. Qt的优点5. Qt的应用场景 二、Qt下载安装 引言 在探索编程和软件开发的旅程中&#xff0c;我们已经奠定了坚实的基础&#xff0c;通过学习C语言和C&#xff0c;我们不仅掌握了结构化编程和面向对象编程的核…

HIVE3.1.3+ZK+Kerberos+Ranger2.4.0高可用集群部署

目录 一、集群规划 二、介质下载 三、基础环境准备 1、解压文件 2、配置环境变量 四、配置zookeeper 1、创建主体 2、修改zoo.cfg 3、新增jaas.conf 4、新增java.env 5、重启ZK 6、验证ZK 五、配置元数据库 六、安装HIVE 1、创建Hiver的kerberso主体 2…

网站笔记:huggingface model memory calculator

Model Memory Utility - a Hugging Face Space by hf-accelerate 这个工具可以计算在 Hugging Face Hub上托管的大型模型训练和执行推理时所需的vRAM内存量。模型所需的最低推荐vRAM内存量表示为“最大层”的大小&#xff0c;模型的训练大约是其大小的4倍&#xff08;针对Adam…

LeetCode 第399场周赛个人题解

100323. 优质数对的总数 I 原题链接 100323. 优质数对的总数 I 思路分析 签到题 AC代码 class Solution:def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:n, m len(nums1), len(nums2)ret 0for i in range(n):for j in range(m):if nu…

关于构建生成式AI产品的思考

在过去的六个月里&#xff0c;我们 LinkedIn 的团队一直在努力开发一种新的人工智能体验。我们希望重新构想我们的会员如何进行求职和浏览专业内容。 生成式人工智能的爆炸式增长让我们停下来思考一年前不可能实现的事情。我们尝试了许多想法&#xff0c;但都没有真正实现&…

1.2 程序员职业发展

目录 1 程序员职业发展方向 2 计算机研究生为何青睐AI赛道 1 程序员职业发展方向 2 计算机研究生为何青睐AI赛道 计算机类研究生&#xff0c;大部分以人工智能作为主赛道&#xff0c;原因如下&#xff1a; 广阔的就业前景&#xff1a;人工智能是当今科技发展的前沿领域&…

7.Redis之String编码方式应用场景业务

1.内部编码 字符串类型的内部编码有 3 种&#xff1a; • int&#xff1a;8 个字节&#xff08;64位&#xff09;的⻓整型。 • embstr&#xff1a;⼩于等于 39 个字节的字符串。压缩字符串.适用于表示比较短的字符串。 • raw&#xff1a;⼤于 39 个字节的字符串。普通字…

Java的线程的使用

一.两种创建线程的方式 1.继承Thread类&#xff08;匿名内部类&#xff09; 创建方式&#xff1a; 1.定义一个子类继承Thread&#xff0c;重写run方法 2.创建子类对象&#xff0c; 3.调用子类对象的start方法&#xff08;启动还是执行的run方法&#xff09; 优缺点&#x…

公安知识学习与题目练习系统

一、系统概述 系统采用C用户小程序端、管理员Web端架构。通过UniappVueSpringboot主流技术实现。具体功能分为&#xff0c;管理侧&#xff1a;可以维护学习知识点、更新知识点详情&#xff1b;C端用户&#xff1a;可以学习知识点、在线刷题练习的功能。次系统在公安专业知识学习…

绘唐科技绘唐ai工具邀请码

绘唐科技绘唐ai工具邀请码 绘唐AI工具 https://qvfbz6lhqnd.feishu.cn/wiki/QBr4wOAz2ilF4NknrqbcoKRhn2c TensorFlow是一个开源的机器学习框架,由Google开发并维护。它提供了一个灵活且高效的接口,用于构建和训练各种机器学习模型。 TensorFlow的基本概念包括: 1. 张量(…

牛客网刷题 | BC99 正方形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

使用Java和XxlCrawler获取各城市月度天气情况实践

目录 前言 一、历史数据获取 1、关于天气后报 2、信息界面分析 二、数据的提取开发 1、PageVo的定义 2、属性定义 3、实际信息抓取 三、信息抓取调试以及可能的问题 1、信息获取成果 2、关于超时的问题 四、总结 前言 这篇文章主要来源于一个我们家小朋友的一个作业…

计算机基础概论:构筑数字社会的硬件基础与交互技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f3af; 引言&#x1f4bb; 什么是计算机&#xff1f;&#x1f331; 计算机的起源与发展&#x1f6e0;️ 电脑硬件的五大核心组件1. 中央处理器 (CPU) - 智慧的心脏2. 随机存取内存 (RAM) - 快速的记忆体3. 存储设备 …

安卓手机听书的各种免费方案

categories: Tips tags: Tips 写在前面 最近 Tencent 突然给微信读书上限制了, 普通用户一个月内仅能导入 3 本书, 这就让经常在 weread 上面听书入眠的我很无奈了. 折腾一下备选方案吧, 肯定是免费优先咯. 下面主要从支持 tts 的阅读器/ tts 免费中文引擎两个角度来讲. r…

Linux驱动(3)- LInux USB驱动层次

在Linux系统中&#xff0c;提供了主机侧和设备侧USB驱动框架。 从主机侧&#xff0c;需要编写USB驱动包括主机控制器驱动&#xff0c;设备驱动两类&#xff0c;USB 主机控制驱动程序控制插入其中的USB设备。 USB设备驱动程序控制该设备如何作为从设备与主机进行通信。 1.主机…

Attention相关问题笔试解析。

Attention相关问题笔试解析。 题目描述一&#xff1a;【选择】题目描述二&#xff1a;【简答】题目描述三&#xff1a;【代码】Scaled Dot-Product Attention&#xff1a;下面是用PyTorch实现的一个Attention机制的代码。这个实现包括一个简单的Scaled Dot-Product Attention机…

XX数字中台技术栈及能力

XX数字中台技术栈及能力 1 概述 XX数字中台面向数据开发者、数据管理者和数据应用者&#xff0c;提供数据汇聚、融合、治理、开发、挖掘、共享、可视化、智能化等能力&#xff0c;实现数据端到端的全生命周期管理&#xff0c;以共筑数字基础底座&#xff0c;共享数据服务能力…