【Spring】深入理解 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/diannao/16388.shtml

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

相关文章

使用AWR对电路进行交流仿真---以整流器仿真为例

使用AWR对电路进行交流仿真—以整流器仿真为例 生活不易&#xff0c;喵喵叹气。马上就要上班了&#xff0c;公司的ADS的版权紧缺&#xff0c;主要用的软件都是NI 的AWR&#xff0c;只能趁着现在没事做先学习一下子了&#xff0c;希望不要裁我。 本AWR专栏只是学习的小小记录而…

6、phpjm混淆解密和php反序列化

题目&#xff1a;青少年雏形系统 1、打开链接也是一个登入面板 2、尝试了sqlmap没头绪 3、尝试御剑&#xff0c;发现一个www.zip 4、下载打开&#xff0c;有一个php文件打开有一段phpjm混淆加密 5、使用手工解混淆 具体解法链接&#xff1a;奇安信攻防社区-phpjm混淆解密浅谈…

音视频开发9 FFmpeg 解复用框架--如何将一个影音文件(mp4文件/wav文件) 最终播放起来

一&#xff0c;播放器框架 二 常用音视频术语 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a; 即特定格式的多媒体文件&#xff0c; 比如mp4、flv、mkv等。 媒体流&#xff08;Stream&#xff09;&#xff1a; 表示时间轴上的一段连续数据&#xff0…

Linux系统进程管理

系统进程管理 一、进程概述 1.1 什么是进程&#xff1f;进程管理需要做什么&#xff1f; 进程是已启动的运行实例&#xff0c;进程有以下组成部分&#xff1a; ​ 已分配内存的地址空间 ​ 进程ID ​ 程序的代码 ​ 进程状态 进程管理包括进程调度、中断处理、信号、进程…

Excel工作簿/表的合并/拆分全集(一文通关)

概述 在工作中&#xff0c;我们常会用到到Excel拆分/合并为多个工作表/簿&#xff0c;如全国的订单表&#xff0c;需要根据省份列拆分下发至对应的省、各省份数据需要汇总、...... 应该如何操作呢&#xff1f; 1. 传统方法&#xff08;借助透视表、Power Query编辑器、VBA实现…

Pytest对协程异步函数进行单元测试

安装 安装基础包 pytest&#xff0c;pytest-asyncio pip install pytest pytest-asyncio测试&#xff1a; pytest -s -v ./python-code/do-async/aiohttp_session_pytest.py书写规范 类名必须以 Test 开头方法和函数名必须以test开头 class TestAddFunc(object): # 测试…

在CentOS7上安装Oracle11

一、概述 Oracle有两种安装方式&#xff0c;桌面安装和静默安装。这里我采用桌面安装的方式。 不得不说&#xff0c;Oracle真的是我目前为止安装过的最麻烦的软件没有之一&#xff0c;比K8S还麻烦&#xff0c;Oracle&#xff0c;真有你的&#xff01;废话不多说&#xff0c;臭…

【Nginx <末>】Nginx 基于 IP 地址的访问限制

目录 &#x1f44b;前言 &#x1f4eb;一、限制 IP 可以实现哪些功能 &#x1f440;二、 项目实现 2.1 访问控制实现 2.2 Nginx 配置中指定 IP 地址 &#x1f49e;️三、章末 &#x1f44b;前言 小伙伴们大家好&#xff0c;前面一段时间学习了 Nginx 的相关知识&#xff0c…

实现排行榜之Mysql的 OrderBy方法

排行榜之Mysql OrderBy实现 1、排行榜系统的功能点 数据收集与计算 排名规则 实时性 可视化展示 周期性更新 2、排行榜系统基本功能要素 MySQL实现方案 数据量较小&#xff0c;业务场景比较简单。可直接使用 新建表 CREATE TABLE leaderboard( id BIGINT UNSIGNED NOT …

《Python源码剖析》之pyc文件

前言 前面我们主要围绕pyObject和pyTypeObject聊完了python的内建对象部分&#xff0c;现在我们将开启新的篇章—python虚拟机&#xff0c;将聚焦在python的执行部分&#xff0c;搞懂从“代码”到“执行”的过程。开启新的篇章之前&#xff0c;你也许会有一个疑惑&#xff1a;我…

泰拉瑞亚从零开始的开服教程

前言 本教程将讲诉使用Linux系统搭建泰拉瑞亚服务器&#xff08;因为网上已经有很完善的windows开服教程了&#xff09;&#xff0c;使用的Linux发行版是Debian11,服务端使用的程序是TShock&#xff0c;游戏版本是1.4.4.9 所需要准备的 一台服务器&#xff08;本教程使用的是…

解决Jupyter运行代码显示Kernel Restarting的错误

在Jupyter notebook上运行代码时发现如下错误&#xff1a; 使用VS Code运行在日志表中发现错误存在&#xff1a; 它表明在初始化"libiomp5md.dll"库时发生问题&#xff0c;因为该库已经被初始化过了&#xff0c;这个错误可能是由于程序中重复初始化OpenMP库导致的&am…

深入探索C++ Vector容器:灵活的动态数组秘籍

目录 ​编辑 引言 一、初识vector&#xff1a;构造与初始化 二、动态管理&#xff1a;添加与删除元素 三、访问与遍历&#xff1a;多种方式直达元素 四、容量与大小&#xff1a;动态调整的艺术 五、进阶技巧&#xff1a;高效运用vector 结语 引言 在C编程的世界里&…

驱动命令之insmod depmod modprobe rmmod modinfo lsmod

insmod命令 insmod需指定所需加载模块的路径&#xff0c;且只加载所指定的模块&#xff0c;如果所指定的模块依赖于其他模块&#xff0c;insmod不会自动添加&#xff1b; 语法 insmod [-fkmpsvxX][-o <模块名称>][模块文件][符号名称 符号值] 参数说明&#xff1a; -f…

微信小程序如何跳转微信公众号

1. 微信小程序如何跳转微信公众号 1.2. 微信公众号配置 登录微信公众号&#xff0c;点击【小程序管理】&#xff1a;   点击【添加】&#xff1a;   点击【关联小程序】&#xff1a;   输入小程序进行关联&#xff1a; 1.2. 微信小程序配置 登录微信小程序&#xf…

HTTP的由来以及发展史

HTML&HTML5的学习探索 01、Html的由来和发展史 01-01、Html的由来 HTML的英文全称是 Hypertext Marked Language&#xff0c;即超文本标记语言。HTML是由Web的发明者 Tim Berners-Lee&#xff08;蒂姆伯纳斯李&#xff09;于1990年创立的一种标记语言&#xff0c; 他是万…

返回枚举类给前端

1. 前言 在实际开发过程中&#xff0c;前端的下拉框或者单选按钮的内容通常的需要和后端匹配的&#xff0c;故一般会由后端将下拉框的内容或单选框的内容传给前端&#xff0c;而这些内容在后端一般是由枚举类存储的&#xff0c;如果后端直接返回枚举类&#xff0c;返回结果将会…

K-means聚类算法详细介绍

目录 &#x1f349;简介 &#x1f348;K-means聚类模型详解 &#x1f348;K-means聚类的基本原理 &#x1f348;K-means聚类的算法步骤 &#x1f348;K-means聚类的优缺点 &#x1f34d;优点 &#x1f34d;缺点 &#x1f348;K-means聚类的应用场景 &#x1f348;K-mea…

SQL Server2019安装步骤教程(图文)_最新教程

一、下载SQL Server2019 1.到微软官网下载SQL Server Developer版本&#xff0c;官网当前的2019版本下载需要注册账号。 不想注册的朋友&#xff0c;可以选择从网盘下载&#xff1a;点击此处直接下载 2.下载之后先解压&#xff0c;解压后执行exe安装程序。打开之后的界面如下…

学 Java 具体能干什么?

学习 Java 后&#xff0c;你可以从事许多不同的工作和项目&#xff0c;涵盖了广泛的应用领域。以下是一些具体的应用场景和工作方向&#xff1a; 1. 企业级应用开发 Java 是企业级应用开发的首选语言之一&#xff0c;特别适合开发大规模、分布式、多层次的企业应用程序。 Jav…