深入理解 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 实例注册到 ServletContext
                        servletContext.addServlet("MyServlet", servlet);
                        // 并映射到指定路径
                        servletContext.getServletRegistration("MyServlet").addMapping("/myservlet");
                    } catch (InstantiationException | IllegalAccessException e) {
                        // 捕获实例化异常
                        // 抛出 ServletException
                        throw new ServletException(e);
                    }
                }
            }
        }
    }
}

三、SpringServletContainerInitializer

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

// 使用 @HandlesTypes 注解指定需要处理的类,即实现了 WebApplicationInitializer 接口的类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    // 当 Servlet 容器启动时调用的方法
    @Override
    public 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) {
                        // 如果实例化失败,则抛出 ServletException
                        throw 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 方法,传入 ServletContext
        for (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 {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { ServletConfig.class };
    }

    @Override
    protected 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年软考总结 信息系统管理师

选择题 英文题&#xff0c;我是一题也没把握&#xff0c;虽然我理解意思。 千万不要认为考死记硬背不对。目的不在于这。工程项目中有很多重要的数字&#xff0c;能记住说明你合格。 案例 几乎把答案全写在案例中了。 计算题 今年最简单。没有考成本。 只考了关键路径&a…

微信小程序毕业设计-智慧旅游平台系统项目开发实战(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

RAG技术综述

RAG的基本架构。&#xff0c;生成器和检索器。 参考paper&#xff1a;https://arxiv.org/html/2402.19473v4 文中将rag的内容从文本扩展至多模态&#xff0c;打开了思路。 生成器&#xff1a;transformer&#xff0c;LSTM&#xff0c;扩散模型&#xff0c;gan 检索器&#xf…

Sourcetree安装教程及使用

1 Sourcetree介绍 Sourcetree是一款免费的Git图形化客户端&#xff0c;它由Atlassian开发&#xff0c;提供了跨平台的支持&#xff0c;可运行在Windows和Mac操作系统上。Sourcetree可以让开发者更方便地使用Git来管理代码&#xff0c;不需要在命令行中输入复杂的Git命令&#x…

【强训笔记】day25

NO.1 思路&#xff1a;哈希质数判断。 代码实现&#xff1a; #include <iostream> #include<string> #include<cmath> using namespace std;bool isprime(int n) {if(n<2) return false;for(int i2;i<sqrt(n);i){if(n%i0) return false;}return true…

Python筑基之旅-运算符

目录 一、运算符 1、了解定义 2、理解意义 2-1、基本数据处理 2-2、条件判断 2-3、逻辑操作 2-4、赋值和更新 2-5、位操作 2-6、提高代码可读性 2-7、解决实际问题 2-8、学习其他编程语言的基础 3、探索方法 3-1、理解概念 3-2、练习基本运算 3-3、掌握优先级 …

ICML 2024 Mamba 论文总结

2024ICML&#xff08;International Conference on Machine Learning&#xff0c;国际机器学习会议&#xff09;在2024年7月21日-27日在奥地利维也纳举行 &#x1f31f;【紧跟前沿】“时空探索之旅”与你一起探索时空奥秘&#xff01;&#x1f680; 欢迎大家关注时空探索之旅 …

ciscn2024(上传一下,有侵权什么的问题的话联系删除)

Web Simple_php 这个Simple_php一点儿也不Simple (⋟﹏⋞) 源码放这儿了&#xff1a; <?phpini_set(open_basedir, /var/www/html/); error_reporting(0);if(isset($_POST[cmd])){$cmd escapeshellcmd($_POST[cmd]); if (!preg_match(/ls|dir|nl|nc|cat|tail|more|flag…

原生标签WebComponent

文章目录 介绍一、web Component二、怎么使用三、在Vue中使用使用场景 前端必备工具推荐网站(免费图床、API和ChatAI等实用工具): http://luckycola.com.cn/ 介绍 平常浏览各个网站过程中&#xff0c;经常遇到的一种现象&#xff1a;页面广告。 这种广告按照来源可分为两种&…

C++面向对象程序设计 - 输入和输出

程序的输入指的是文件将数据传送给程序&#xff0c;程序的输出指的是从程序将数据传送输出文件。 C的输入和和输出包括以下三个方面&#xff1a; 对系统指定的标准设备的输入和输出&#xff0c;即从键盘输入数据&#xff0c;输出到显示器屏幕。以外存磁盘&#xff08;或光盘、…

车道线识别与预警系统LDWS(代码+教程)

车道线识别与预警系统&#xff08;Lane Departure Warning System, LDWS&#xff09;作为智能交通系统中的重要组成部分&#xff0c;旨在通过先进的图像处理和计算机视觉技术&#xff0c;实时监测车辆行驶过程中的车道位置&#xff0c;预防因驾驶员疏忽或疲劳导致的车道偏离事故…

Flink 窗口

窗口&#xff08;Window&#xff09; 窗口是处理无限流的核心。 窗口将流分割成有限大小的“桶”&#xff0c;我们可以计算窗口中的数据。 窗口程序一般有键控流&#xff08;keyed streams&#xff09;的窗口程序 和 非键控流&#xff08;non-keyed streams&#xff09;的窗口…

Selenium 自动化测试工具(1) (Selenium 工作原理,常用API的使用)

文章目录 什么是自动化测试什么是测试工具&#xff1a;Selenium 工作原理(重要)Selenium API定位元素CSS 选择器xpath 定位元素 通过Java代码实现自动化1. 定位元素2. 关闭浏览器3. 获取元素文本4. 鼠标点击与键盘输入5. 清空内容6.打印信息 什么是自动化测试 关于自动化&…

一个人应该怎么操作抖音小店呢?店铺操作流程给你讲解清楚!

大家好&#xff0c;我是电商小V 现在入驻抖音小店的有很多新手&#xff0c;新手最关心的就是一个人应该如何操作抖音小店&#xff0c;操作抖音小店需要做好哪几步呢&#xff1f;关于这个问题咱们就来详细的讲解一下&#xff0c; 第一点&#xff1a;开店 开店是做店的第一步&…

【Codesys】-执行第三方程序,或Windows脚本

该记录旨在解决RTE作为第一个Windows的一个exe程序不能调用其他程序的问题。 可以实现:在PLC界面打开第三方程序、在PLC界面关闭本机Windows操作系统 首先添加依赖库-SysProcess,3.5.17.0 然后在程序里执行相应的指令&#xff0c;该指令可以被Windows识别为类似于执行Bat文件…

搭建企业级AI应用的流程

搭建企业级AI应用的流程是一个复杂且系统化的工程&#xff0c;它需要从多个维度出发&#xff0c;确保最终的应用既符合企业的业务需求&#xff0c;也具备高效、稳定和可扩展的特性。以下是详细的步骤&#xff1a; 初步接触与需求分析是整个项目的基础。在这一阶段&#xff0c;我…

群晖安装青龙脚本

青龙定时任务管理面板&#xff0c;支持 Python3、JavaScript、Shell、Typescript 这几种环境&#xff0c;通过它可以方便的管理和运行定时任务&#xff08;在某个时间执行一段代码&#xff09;&#xff0c;并且只需简单的配置&#xff0c;就可以在各个平台收到任务执行的结果通…

通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称、用icon区分文件和文件夹等需求

目录 一、通过el-tree自定义渲染网页版工作目录 1.1、需求介绍 1.2、使用el-tree生成文档目录 1.2.1、官方基础用法 ①效果 ②代码&#xff1a; 1.2.2、自定义文档目录&#xff08;实现鼠标悬浮显示完整名称、用icon区分文件和文件夹&#xff09; ①效果&#xff08;直接效…

Elasticsearch 分析器的高级用法一(同义词,高亮搜索)

Elasticsearch 分析器的高级用法一&#xff08;同义词&#xff0c;高亮搜索&#xff09; 同义词简介分析使用同义词案例 高亮搜索高亮搜索策略unifiedplainvh 同义词 简介 在搜索场景中&#xff0c;同义词用来处理不同的查询词&#xff0c;有可能是想表达相同的搜索目标。 例…

WIFI国家码设置的影响

记录下工作中关于国家码设置对WIFI的影响&#xff0c;以SKYLAB的SKW99和SDZ202模组为例进行说明。对应到日常&#xff0c;就是我们经常提及手机是“美版”“港版”等&#xff0c;它们的wifi国家码是不同的&#xff0c;各版本在wifi使用中遇到的各种情况与下面所述是吻合的。 现…