spring mvc源码学习笔记之十
前面的文章介绍了用 WebApplicationInitializer
或者 AbstractAnnotationConfigDispatcherServletInitializer
来代替 web.xml
。
我们学 java web 的时候就知道,servlet 容器会自动加载 web.xml
。
那么,疑问就来了,WebApplicationInitializer
或者 AbstractAnnotationConfigDispatcherServletInitializer
既然替代了 web.xml
,那应该也会被 servlet 容器加载,是不是这样呢?答案是:是的。
而完成这个加载工作的是 SpringServletContainerInitializer
类。看下它的源码:
package org.springframework.web; import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils; /**
* 本类继承了 servlet 3.0 的 ServletContainerInitializer 接口。
* 下面这段话的意思是:
* servlet 3.0 的 ServletContainerInitializer 接口是用来让 servlet 容器支持基于编码的配置。
* 在这个过程中要结合 spring 的 WebApplicationInitializer。
* 而 spring 的 WebApplicationInitializer 可以替换 web.xml 也可以结合 web.xml 一起使用。
*
* Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based
* configuration of the servlet container using Spring's {@link WebApplicationInitializer}
* SPI as opposed to (or possibly in combination with) the traditional
* {@code web.xml}-based approach.
*
* 当 servlet 容器启动的时候,如果累路径下有 spring-web 的话,这个类会被加载,并且 onStartup 方法会被调用。
* 这背后的原理在于 java 的 SPI 机制。
* spring-web 模块下有个 META-INF/services/javax.servlet.ServletContainerInitializer 文件,
* 其内容就是当前类的完全限定名。
*
* <h2>Mechanism of Operation</h2>
* This class will be loaded and instantiated and have its {@link #onStartup}
* method invoked by any Servlet 3.0-compliant container during container startup assuming
* that the {@code spring-web} module JAR is present on the classpath. This occurs through
* the JAR Services API {@link ServiceLoader#load(Class)} method detecting the
* {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer}
* service provider configuration file. See the
* <a href="http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider">
* JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0
* Final Draft specification for complete details.
*
* <h3>In combination with {@code web.xml}</h3>
* A web application can choose to limit the amount of classpath scanning the Servlet
* container does at startup either through the {@code metadata-complete} attribute in
* {@code web.xml}, which controls scanning for Servlet annotations or through an
* {@code <absolute-ordering>} element also in {@code web.xml}, which controls which
* web fragments (i.e. jars) are allowed to perform a {@code ServletContainerInitializer}
* scan. When using this feature, the {@link SpringServletContainerInitializer}
* can be enabled by adding "spring_web" to the list of named web fragments in
* {@code web.xml} as follows:
*
* <pre class="code">
* {@code
* <absolute-ordering>
* <name>some_web_fragment</name>
* <name>spring_web</name>
* </absolute-ordering>
* }</pre>
*
* SpringServletContainerInitializer 可以看做一个简单的代理,真正的工作还是交给了 WebApplicationInitializer 。
*
* <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2>
* Spring's {@code WebApplicationInitializer} SPI consists of just one method:
* {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally
* quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}:
* simply put, {@code SpringServletContainerInitializer} is responsible for instantiating
* and delegating the {@code ServletContext} to any user-defined
* {@code WebApplicationInitializer} implementations. It is then the responsibility of
* each {@code WebApplicationInitializer} to do the actual work of initializing the
* {@code ServletContext}. The exact process of delegation is described in detail in the
* {@link #onStartup onStartup} documentation below.
*
* <h2>General Notes</h2>
* In general, this class should be viewed as <em>supporting infrastructure</em> for
* the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
* advantage of this container initializer is also completely <em>optional</em>: while
* it is true that this initializer will be loaded and invoked under all Servlet 3.0+
* runtimes, it remains the user's choice whether to make any
* {@code WebApplicationInitializer} implementations available on the classpath. If no
* {@code WebApplicationInitializer} types are detected, this container initializer will
* have no effect.
*
* <p>Note that use of this container initializer and of {@code WebApplicationInitializer}
* is not in any way "tied" to Spring MVC other than the fact that the types are shipped
* in the {@code spring-web} module JAR. Rather, they can be considered general-purpose
* in their ability to facilitate convenient code-based configuration of the
* {@code ServletContext}. In other words, any servlet, listener, or filter may be
* registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific
* components.
*
* 注意:不要扩展这个类,也不要继承这个类。
* 这个类应该被视为内部类型,不对外。
* 对外的是 WebApplicationInitializer。
*
* <p>This class is neither designed for extension nor intended to be extended.
* It should be considered an internal type, with {@code WebApplicationInitializer}
* being the public-facing SPI.
*
* <h2>See Also</h2>
* See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
* recommendations.<p>
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 3.1
* @see #onStartup(Set, ServletContext)
* @see WebApplicationInitializer
*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer { { System.out.println("@HandlesTypes(WebApplicationInitializer.class) 这个注解表明了当前类 SpringServletContainerInitializer "+ " 需要处理的类型是 WebApplicationInitializer ");
} /**
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
* implementations present on the application classpath.
*
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
* Servlet 3.0+ containers will automatically scan the classpath for implementations
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
* such types to the {@code webAppInitializerClasses} parameter of this method.
* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
* this method is effectively a no-op. An INFO-level log message will be issued notifying
* the user that the {@code ServletContainerInitializer} has indeed been invoked but that
* no {@code WebApplicationInitializer} implementations were found.
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
* they will be instantiated (and <em>sorted</em> if the @{@link
* org.springframework.core.annotation.Order @Order} annotation is present or
* the {@link org.springframework.core.Ordered Ordered} interface has been
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
* method will be invoked on each instance, delegating the {@code ServletContext} such
* that each instance may register and configure servlets such as Spring's
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
* or any other Servlet API componentry such as filters.
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
* @see WebApplicationInitializer#onStartup(ServletContext)
* @see AnnotationAwareOrderComparator
*/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException { servletContext.log("SpringServletContainerInitializer 利用了 java 的 SPI 机制"); List<WebApplicationInitializer> initializers = new LinkedList<>(); if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
} System.out.println("SpringServletContainerInitializer 在其 onStartup 方法中检测类路径下的 WebApplicationInitializer(spring的) "); if (initializers.isEmpty()) {
servletContext.log("在类路径下没有检测到 spring 的 WebApplicationInitializer。------- No Spring WebApplicationInitializer types detected on classpath");
return;
} System.out.println("在类路径下检测到了 " + initializers.size() + " 个 spring 的 WebApplicationInitializer。"); servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); System.out.println("依次调用 WebApplicationInitializer 的 onStartup 方法"); for (WebApplicationInitializer initializer : initializers) { System.out.println("调用 " + initializer.getClass().getName() + " 的 onStartup 方法"); initializer.onStartup(servletContext);
} System.out.println("至此,通过 servlet、java spi 成功引导了 spring "); } }
关于这个类有几个需要注意的点:
- 它实现了 servlet 3.0 的
javax.servlet.ServletContainerInitializer
接口 - 它加了个
@HandlesTypes(WebApplicationInitializer.class)
注解 - 它实现了
javax.servlet.ServletContainerInitializer
接口中定义的onStartup
方法
总结下来。SpringServletContainerInitializer
这个类实现了 servlet 3.0 的 javax.servlet.ServletContainerInitializer
接口,这就决定了在 servlet 容器启动的时候 onStartUp
方法会被自动触发,而在 onStartUp
方法内部 WebApplicationInitializer
的 onStartUp
方法被调用。这就是 WebApplicationInitializer
被容器带起来的过程。
这里只是说了个大概,要想非常清楚,还请自己研究下 servlet 3.0 的 javax.servlet.ServletContainerInitializer
接口。
相关文章:
spring mvc源码学习笔记之十
前面的文章介绍了用 WebApplicationInitializer 或者 AbstractAnnotationConfigDispatcherServletInitializer 来代替 web.xml 。 我们学 java web 的时候就知道,servlet 容器会自动加载 web.xml。 那么,疑问就来了,WebApplicationInitialize…...

Ubuntu 下载安装 elasticsearch7.17.9
参考 https://blog.csdn.net/qq_26039331/article/details/115024218 https://blog.csdn.net/mengo1234/article/details/104989382 过程 来到 Es 的版本发布列表页面:https://www.elastic.co/downloads/past-releases#elasticsearch 根据自己的系统以及要安装的…...
Qt笔记:网络编程Tcp
一、铺垫 1.以下只是告诉诸位怎样去构建服务器与客户端;客户端这样构建肯定没问题;但是服务端不可能这样写,因为他是布置在Linux上的,纯数据类处理服务器,根本不可能用Qt写;这在Qt的http类中就表明了&…...
C++单例模式跨DLL调用问题梳理
问题案例: 假设有这样一个单例模式的代码 //test.h header class Test { public:static Test &instance() {static Test ins;return ins;}void foo(); };void testFoo();//test.cpp source #include "test.h"void Test::foo() {printf("%p\n&q…...

oracle闪回版本查询
闪回版本查询(Flashback Versions Query)是Oracle数据库提供的一种功能,允许用户查看某个表在特定时间范围内的所有版本。这对于审计和调试数据修改问题非常有用。通过闪回版本查询,你可以了解表中的数据在某个时间段内的变化历史…...

C#用winform窗口程序操作服务+不显示Form窗体,只显示右下角托盘图标+开机时自启动程序【附带项目地址】
服务的文章在:https://blog.csdn.net/weixin_43768573/article/details/144957941 一、用winform窗口程序操作服务 1、点击“创建新项目”,选择“Windows 服务(.NET Framework)” 2、给项目命名 3、右击项目->添加->新建项,选择“应用程序清单文件(仅限Windo…...

UOS系统和windows系统wps文档显示差异问题解决
最近在使用UOS系统的过程中,发现了一个很有意思的现象。就是在UOS系统上编辑的文档,发到windows系统上,会出现两个文档显示差异很大的情况,文档都是使用一样的wps软件打开的。到底是什么原因导致这种现象的呢?该如何解…...

JS中函数基础知识之查漏补缺(写给小白的学习笔记)
函数 函数是ECMAScript中 最有意思的部分之一, 主要是因为函数实际上是对象.-- 每个函数 都是Function类型的实例,Function也有属性和方法. 因为函数是对象,所以函数名就是指向函数对象的指针. 常用的定义函数的语法: ①函数声明 ②函数表达式 ③箭头函数 function sum (n…...
蓝桥杯训练
1对于一个字母矩阵,我们称矩阵中的一个递增序列是指在矩阵中找到两个字母,它们在同一行,同一列,或者在同一 45 度的斜线上,这两个字母从左向右看、或者从上向下看是递增的。 例如,如下矩阵中 LANN QIAO有…...

前端学习DAY33(外边距的折叠)
垂直外边距的重叠 在网页中相邻的垂直方向的外边距,会发生外边距的重叠 兄弟元素 兄弟元素之间的相邻外边距会取(绝对值)最大值,而不是取和,谁大取谁 特殊情况:如果相邻的外边距一正一负,则取两…...
asp.net core mvc的 ViewBag , ViewData , Module ,TempData
在 ASP.NET MVC 和 ASP.NET Core MVC 中,ViewBag 和 ViewData 是两种用于将数据从控制器传递到视图(View)的常用方法。它们都允许控制器将动态数据传递给视图,但它们的实现方式有所不同。关于 Module,它通常指的是某种…...

Linux驱动学习之第二个驱动程序(LED点亮关闭驱动程序-分层设计思想,使其能适应不同的板子-驱动程序模块为多个源文件怎么写Makefile)
目录 看这篇博文前请先掌握下面这些博文中的知识需要的PDF资料完整源代码board_fire_imx6ull-pro.c中的代码leddrv.c中的代码ledtest.c中的代码 程序设计思想和文件结构实现分层思想的具体方法概述具体实现分析定义结构体led_operations用来集合各个单板硬件层面操作LED的函数定…...
手写@EnableTransactionalManagement
定义一个注解,用于标注于方法上,标志着此方法是一个事务方法。 Target({ElementType.METHOD,ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) public interface MyTransaction {}定义一个开启事务功能的注解 Component Import(TransActionBean…...
【Vue】:解决动态更新 <video> 标签 src 属性后视频未刷新的问题
问题描述 在 Vue.js 项目,当尝试动态更新 <video> 标签的 <source> 元素 src 属性来切换视频时,遇到了一个问题:即使 src 属性已更改,浏览器仍显示旧视频。具体表现为用户选择新视频后,视频区域继续显示之…...

网络基础1 http1.0 1.1 http/2的演进史
http1.0 1.1 http/2的演进史😎 (连接复用 队头阻塞 服务器推送 2进制分帧) 概述 我们主要关注的是应用层 传输层 http协议发展历史 http的报文结构:起始行 Header Body http的典型特征 http存在的典型问题 Keep Alive机制 chun…...
Python 通过命令行在 unittest.TestCase 中运行单元测试
文章目录 unittest 模块简介编写单元测试在命令行中运行所有测试在命令行中运行单个测试使用装饰器跳过测试总结常用断言方法 unittest 模块简介 unittest是Python标准库中的一个模块,用于编写和运行单元测试。它提供了一个单元测试框架,使得编写测试用…...

源代码编译安装X11及相关库、vim,配置vim(2)
一、编译安装vim 编译时的cofigure选项如下.只有上一步的X11的包安装全了(具体哪些是必须的,哪些是多余的没验证),configure才能认为X的库文件和头文件是可以用的。打开多个编程语言的支持特性。 ./configure --prefixpwd/mybui…...

设计模式 行为型 观察者模式(Observer Pattern)与 常见技术框架应用 解析
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新。 一…...

【25考研】川大计算机复试情况,重点是啥?怎么准备?
24年进入复试的同学中,有10位同学的复试成绩为0分。具体是个人原因还是校方原因,还尚不明确。但是C哥提醒,一定要认真复习!复试完后不要跟任何人讨论有关复试的题目及细节! 一、复试内容 四川大学复试内容较多…...

C#调用Lua
目录 xLua导入 打包工具导入 单例基类导入与AB包管理器导入 Lua解析器 文件加载与重定向 Lua解析器管理器 全局变量获取 全局函数获取 对于无参数无返回值 对于有参数有返回值 对于多返回值 对于变长参数 完整代码 List与Dictionary映射Table 类映射Table 接口映射…...
什么是 Ansible 主机和组变量
Ansible 是一款强大的自动化工具,可简化配置管理、应用程序部署和预配等 IT 任务。其最有价值的功能之一是能够定义变量,从而为不同的主机和组定制剧本。本文将解释 Ansible 中组变量和主机变量的概念,并通过实际示例说明它们的用法。 Ansib…...
服务器新建用户无法使用conda
服务器新建用户无法使用conda 1.将.bashrc文件复制到新用户家目录下 sudo cp .bashrc /home/newuser/.bashrc2.source命令激活该文件 source ~/.bashrc3.将.condarc文件复制到新用户家目录下 sudo cp .condarc/home/newuser/.condarc...
STM32开发中,线程启动异常问题排查简述
1. 参数传递问题 错误类型:线程属性错误地使用。影响:线程属性(如堆栈大小、优先级)不匹配可能导致线程创建失败或行为异常。验证方法:检查 线程创建的返回值,若为 NULL 则表示线程创建失败。 2. 系统资源…...
“组件、路由懒加载”,在 Vue3 和 React 中分别如何实现? (copy)
Vue3 和 React 组件懒加载实现方式 React 中组件懒加载的实现方式 React 提供了 React.lazy 和 Suspense 两个 API 来实现组件的懒加载。React.lazy 用于动态导入组件,而 Suspense 则用于指定加载过程中的占位内容。例如,可以通过以下代码实现懒加载&a…...

阿里云服务器安装nginx并配置前端资源路径(前后端部署到一台服务器并成功访问)
运行以下命令,安装Nginx相关依赖。 yum install -y gcc-c yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum install -y openssl openssl-devel 运行wget命令下载Nginx 1.21.6。 您可以通过Nginx开源社区直接获取对应版本的安装包URL&…...
将数据库表导出为C#实体对象
数据库方式 use 数据库;declare TableName sysname 表名 declare Result varchar(max) /// <summary> /// TableName /// </summary> public class TableName {select Result Result /// <summary>/// CONVERT(NVARCHAR(500), ISNULL(ColN…...

使用 Coze 工作流一键生成抖音书单视频:全流程拆解与技术实现
使用 Coze 工作流一键生成抖音书单视频:全流程拆解与技术实现(提供工作流) 摘要:本文基于一段关于使用 Coze 平台构建抖音爆火书单视频的详细讲解,总结出一套完整的 AI 视频自动化制作流程。内容涵盖从思路拆解、节点配…...
vue中的派发事件与广播事件,及广播事件应用于哪些场景和一个表单验证例子
在 Vue 2.X 中,$dispatch 和 $broadcast 方法已经被废弃。官方认为基于组件树结构的事件流方式难以理解,并且在组件结构扩展时容易变得脆弱。因此,Vue 2.X 推荐使用其他方式来实现组件间的通信,例如通过 $emit 和 $on 方法&#x…...

Sklearn 机器学习 缺失值处理 填充数据列的缺失值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 💡使用 Scikit-learn 处理数据缺失值的完整指南 在机器学习项目中,数据缺失是不可避…...
Linux与Windows切换使用Obsidian,出现 unexplained changes 问题的解决
如果你的Obsidian文档在Linux与Windows间来回切换,可能会涉及到文件的保存换行符问题,但这样的话就容易导致一个问题,那就是内容无差异,Obsidian却提示unexplained changes,Windows系统下的解决方法如下,找…...