spring mvc源码学习笔记之十

news2025/1/10 0:53:08

前面的文章介绍了用 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 方法内部 WebApplicationInitializeronStartUp 方法被调用。这就是 WebApplicationInitializer 被容器带起来的过程。

这里只是说了个大概,要想非常清楚,还请自己研究下 servlet 3.0 的 javax.servlet.ServletContainerInitializer 接口。

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

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

相关文章

QT实现 端口扫描暂停和继续功能 3

上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件&#xff0c;功能为暂停扫描&#xff0c;并在暂停后显示继续按钮&#xff0c;点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…

LabVIEW瞬变电磁接收系统

利用LabVIEW软件与USB4432采集卡开发瞬变电磁接收系统。系统通过改进硬件配置与软件编程&#xff0c;解决了传统仪器在信噪比低和抗干扰能力差的问题&#xff0c;实现了高精度的数据采集和处理&#xff0c;特别适用于地质勘探等领域。 ​ 项目背景&#xff1a; 瞬变电磁法是探…

左神算法基础巩固--3

文章目录 二叉树二叉树的遍历先序遍历中序遍历后序遍历 解答二叉树的宽度优先遍历 在这里插入图片描述 一颗完全二叉树具有以下特征&#xff1a;1.不存在任何一个节点具有右子树但不存在左子树.2.不存在任何一个节点在满足1的情况下左右子树不全且其后续节点不为叶子节点 根据以…

高山旅游景区有效降低成本,无人机山下到山上物资吊运技术详解

在高山旅游景区&#xff0c;传统的物资运输方式往往面临人力成本高昂、效率低下等问题&#xff0c;而无人机技术的引入为这一难题提供了新的解决方案。以下是对无人机从山下到山上进行物资吊运技术的详细解析&#xff1a; 一、无人机物资吊运技术的优势 1. 降低人力成本&#…

APP上架之Android 证书 MD5 指纹

Android 证书 MD5 指纹 1. 什么是 Android 证书 MD5 指纹&#xff1f; Android 证书 MD5 指纹是对证书数据进行 MD5 哈希运算后得到的 128 位字符串。在 Android 开发中&#xff0c;每个证书在理论上都有一个唯一的 MD5 指纹&#xff0c;用于识别和验证证书的有效性。证书指纹…

用户界面的UML建模11

然而&#xff0c;在用户界面方面&#xff0c;重要的是要了解《boundary》类是如何与这个异常分层结构进行关联的。 《exception》类的对象可以作为《control》类的对象。因此&#xff0c;《exception》类能够聚合《boundary》类。 参见图12&#xff0c;《exception》Database…

网络安全-XSS跨站脚本攻击(基础篇)

漏洞扫描的原理 1.跨站脚本攻击介绍 xss跨站脚本攻击&#xff1a; xSS 全称&#xff08;Cross site Scripting &#xff09;跨站脚本攻击&#xff0c;是最常见的Web应用程序安全漏洞之一&#xff0c;位于OWASP top 10 2013/2017年度分别为第三名和第七名&#xff0c;XSS是指攻…

CODESYS MODBUS TCP通信(禾川Q1 PLC作为MODBUS TCP从站)

禾川Q1 PLC MODBUS TCP 通信(PLC作为MODBUS TCP通信主站) 禾川Q1 PLC MODBUS TCP通信(CODESYS平台完整配置+代码)-CSDN博客文章浏览阅读28次。MATLAB和S7-1200PLC水箱液位高度PID控制联合仿真(MODBUSTCP通信)_将matlab仿真导入plc-CSDN博客文章浏览阅读722次。本文详细介绍了如…

golang OpcUaClient

实现功能 package mainimport ("fmt""log""opcuaclient/util/plugin/client/opcclient""os""os/signal""syscall" )func main() {OPCUATest()// 监听操作系统信号&#xff0c;阻塞直到接收到信号quit : make(chan…

git commit冲突,需输入提交信息合并提交

git commit时冲突&#xff0c;需输入提交信息合并提交&#xff0c;该如何操作&#xff1f; windows按esc键进入命令模式&#xff0c;输入&#xff1a;wq并按enter保存并退出即可。

Linux/Ubuntu/银河麒麟 arm64 飞腾FT2000 下使用 arm64版本 linuxdeployqt 打包Qt程序

文章目录 一、前言二、环境三、准备1、下载Linuxdeployqt源码2、下载Appimagetool-aarch64.AppImage四、编译linuxdeployqt1.配置环境变量2.编译linuxdeployqt五、安装patchelf六、配置Appimagetool七、打包Qt程序重要提示:测试启动应用八、其他九、最后一、前言 因为项目需要…

操作系统大题整理

专题一 程序代码题&#xff1a;程序设计与分析&#xff0c;主要考的是线程&#xff0c;多线程的并发&#xff1f; 大题第一问&#xff08;1&#xff09;操作系统的结构有哪几种常用的结构&#xff1f; 宏内核&#xff1a;宏内核是将操作系统的主要功能模块都集中在内核的一种结…

SQL编程语言

第一章 1. 数据库是长期储存在计算机内&#xff0c;由专门的数据管理软件(数据库管理系统)&#xff0c;进行统一组织和管理控制的大量数据的集合。 2.数据库的基本特点不包括可以快速检索。 3. 数据管理技术的发展经历了&#xff1a;人工管理阶段、文件系统阶段、数据库系统阶…

【跨域问题】

跨域问题 官方概念&#xff1a; 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域本质来说&#xff0c;是前端请求给到后端时候&#xff0c;请求头里面&#xff0c;有一个 Origin &#xff0c;会带上 协议域名端口号等&#xff1b;后端接受到请求&…

Linux(CentOS7)安装JDK和Maven

文章目录 CentOS软件安装方式JDK安装Maven安装 CentOS软件安装方式 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可。例如tomcatrpm(redhat package manager)安装软件已经按照redhat的包管理规范进行打包&#x…

RabbitMQ 可观测性最佳实践

RabbitMQ 简介 RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写&#xff0c;支持多种客户端。它通过使用交换机&#xff08;Exchanges&#xff09;、队列&#xff08;Queues&#xff09;和绑定&#xff08;Bindings&#xff09;来路由消息&#xff…

Github Copilot学习笔记

&#xff08;一&#xff09;Prompt Engineering 利用AI工具生成prompt设计好的prompt结构使用MarkDown语法&#xff0c;按Role, Skills, Constrains, Background, Requirements和Demo这几个维度描述需求。然后收输入提示词&#xff1a;作为 [Role], 拥有 [Skills], 严格遵守 […

单片机-定时器中断

1、相关知识 振荡周期1/12us; //振荡周期又称 S周期或时钟周期&#xff08;晶振周期或外加振荡周期&#xff09;。 状态周期1/6us; 机器周期1us; 指令周期1~4us; ①51单片机有两组定时器/计数器&#xff0c;因为既可以定时&#xff0c;又可以计数&#xff0c;故称之为定时器…

高比例压缩:Linux 中的压缩命令与技巧

文章目录 高比例压缩&#xff1a;Linux 中的压缩命令与技巧1. 压缩格式的选择2. gzip 命令示例&#xff1a;压缩文件示例&#xff1a;解压文件 3. bzip2 命令示例&#xff1a;压缩文件示例&#xff1a;解压文件 4. xz 命令示例&#xff1a;压缩文件示例&#xff1a;解压文件 5.…

【ArcGIS Pro二次开发实例教程】(1):图层的前置、后置

一、简介 此工具要实现的功能是&#xff1a;将内容框中当前选定的图层移到最顶层或最底层。 主要技术要点包括&#xff1a; 1、Config.daml文件设置&#xff08;UI设置&#xff09; 2、按钮的图片和位置设置 3、当前选定图层的获取 4、图层在内容列表中位置的获取和移动 …