SpringMVC DispatcherServlet源码(6) 完结 静态资源原理
阅读源码,分析静态资源处理器相关组件:
- 使用SimpleUrlHandlerMapping管理url -> 处理器映射关系
- spring mvc使用WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件
- DelegatingWebMvcConfiguration可以使用WebMvcConfigurer的配置静态资源url pattern
- spring boot使用EnableWebMvcConfiguration类读取配置参数,然后ResourceHandlerRegistry添加静态资源url pattern
- SimpleUrlHandlerMapping中封装ResourceHttpRequestHandler对象作为请求处理器
- HttpRequestHandlerAdapter支持HttpRequestHandler类型的请求处理器
本文将对上述6点内容展开分析。
注入HandlerMapping
WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件
在spring mvc中,静态资源也使用HandlerMapping管理映射关系,只是使用的实现类不一样:
- 业务请求使用的HandlerMapping实现类是RequestMappingHandlerMapping类
- 静态资源使用的HandlerMapping实现类是SimpleUrlHandlerMapping类
注入SimpleUrlHandlerMapping对象的入口在WebMvcConfigurationSupport的resourceHandlerMapping方法中:
// Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped resource handlers.
// To configure resource handling, override addResourceHandlers.
@Bean
public HandlerMapping resourceHandlerMapping(@Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,@Qualifier("mvcPathMatcher") PathMatcher pathMatcher,@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,this.servletContext, contentNegotiationManager, urlPathHelper);// Add resource handlers for serving static resources// 这是一个抽象方法,用于向registry添加静态资源路径pattern// * 一个是spring mvc的DelegatingWebMvcConfiguration类// * 一个是spring boot的WebMvcAutoConfiguration.EnableWebMvcConfiguration类// * 两者的本质就是使用registry.addResourceHandler添加静态资源路径patternaddResourceHandlers(registry);// 根据添加的静态资源路径pattern创建HandlerMapping// 里面创建的是SimpleUrlHandlerMapping类型对象AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();if (handlerMapping == null) {return null;}handlerMapping.setPathMatcher(pathMatcher);handlerMapping.setUrlPathHelper(urlPathHelper);handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));handlerMapping.setCorsConfigurations(getCorsConfigurations());return handlerMapping;
}
下文将介绍:
- addResourceHandlers的实现
- registry创建SimpleUrlHandlerMapping的方式
DelegatingWebMvcConfiguration的addResourceHandlers方法
protected void addResourceHandlers(ResourceHandlerRegistry registry) {this.configurers.addResourceHandlers(registry);
}
configurers是WebMvcConfigurerComposite对象,内部封装这容器里面的所有WebMvcConfigurer组件集:
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}
}
当调用this.configurers.addResourceHandlers时,WebMvcConfigurerComposite又会通过WebMvcConfigurer添加静态资源路径pattern:
public void addResourceHandlers(ResourceHandlerRegistry registry) {for (WebMvcConfigurer delegate : this.delegates) {// 这里通过WebMvcConfigurer调用扩展逻辑,添加静态资源路径patterndelegate.addResourceHandlers(registry);}
}
例如:
@Component
public class AppConfig implements WebMvcConfigurer {// ...@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("/static/");}// ...
}
EnableWebMvcConfiguration的addResourceHandlers方法
protected void addResourceHandlers(ResourceHandlerRegistry registry) {// 调用父类方法,也就是使用WebMvcConfigurer添加静态资源patternsuper.addResourceHandlers(registry);if (!this.resourceProperties.isAddMappings()) {return;}addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");// spring boot的spring.mvc.staticPathPattern配置参数// spring boot的spring.resources.staticLocations配置参数addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(),this.resourceProperties.getStaticLocations());
}private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {if (registry.hasMappingForPattern(pattern)) {return;}// 添加静态资源patternResourceHandlerRegistration registration = registry.addResourceHandler(pattern);registration.addResourceLocations(locations);registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());customizeResourceHandlerRegistration(registration);this.autoConfiguredResourceHandlers.add(pattern);
}
WebMvcConfigurer接口
定义callback方法,Spring MVC通过这些方法扩展组件:
public interface WebMvcConfigurer {// 配置PathMatchConfigurerdefault void configurePathMatch(PathMatchConfigurer configurer) {}// Configure content negotiation optionsdefault void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}// Configure asynchronous request handling optionsdefault void configureAsyncSupport(AsyncSupportConfigurer configurer) {}// 配置DefaultServletHttpRequestHandlerdefault void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}// Add Converters and Formatters in addition to the ones registered by defaultdefault void addFormatters(FormatterRegistry registry) {}// 添加拦截器default void addInterceptors(InterceptorRegistry registry) {}// 添加静态资源处理器default void addResourceHandlers(ResourceHandlerRegistry registry) {}// cors配置default void addCorsMappings(CorsRegistry registry) {}// 添加ViewControllerdefault void addViewControllers(ViewControllerRegistry registry) {}// 配置ViewResolverdefault void configureViewResolvers(ViewResolverRegistry registry) {}// 添加参数解析器default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}// 添加返回值处理器default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}// 配置消息转换器default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}// 扩展消息转换器default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}// 配置异常处理器default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}// 扩展异常处理器default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
}
创建SimpleUrlHandlerMapping对象
protected AbstractHandlerMapping getHandlerMapping() {Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();for (ResourceHandlerRegistration registration : this.registrations) {for (String pathPattern : registration.getPathPatterns()) {// * 请注意这里使用的是ResourceHttpRequestHandlerResourceHttpRequestHandler handler = registration.getRequestHandler();if (this.pathHelper != null) {handler.setUrlPathHelper(this.pathHelper);}if (this.contentNegotiationManager != null) {handler.setContentNegotiationManager(this.contentNegotiationManager);}handler.setServletContext(this.servletContext);handler.setApplicationContext(this.applicationContext);try {handler.afterPropertiesSet();} catch (Throwable ex) {throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);}urlMap.put(pathPattern, handler);}}// 创建SimpleUrlHandlerMappingreturn new SimpleUrlHandlerMapping(urlMap, this.order);
}
SimpleUrlHandlerMapping类
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
使用一个Map<String, Object>管理url pattern -> 处理器对象的映射关系。
从上文可以知道,静态资源映射使用的是ResourceHttpRequestHandler类型的处理器。
ResourceHttpRequestHandler类
这个类实现了HttpRequestHandler接口:
@FunctionalInterface
public interface HttpRequestHandler {void handleRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException;
}
ResourceHttpRequestHandler的代码实现:
public void handleRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {Resource resource = getResource(request);if (resource == null) {// 404response.sendError(HttpServletResponse.SC_NOT_FOUND);return;}if (HttpMethod.OPTIONS.matches(request.getMethod())) {response.setHeader("Allow", getAllowHeader());return;}// Supported methods and required sessioncheckRequest(request);// Header phaseif (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {return;}// Apply cache settings, if anyprepareResponse(response);// Check the media type for the resourceMediaType mediaType = getMediaType(request, resource);setHeaders(response, resource, mediaType);// Content phaseServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);// 不是range请求if (request.getHeader(HttpHeaders.RANGE) == null) {// 把资源写出去this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);} else {ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);try {// range请求List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);// 把资源写出去this.resourceRegionHttpMessageConverter.write(HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);} catch (IllegalArgumentException ex) {response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);}}
}
HttpRequestHandlerAdapter类
前面的文章已经介绍,HttpRequestHandlerAdapter负责处理HttpRequestHandler类型的处理器:
public class HttpRequestHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return (handler instanceof HttpRequestHandler);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {((HttpRequestHandler) handler).handleRequest(request, response);return null;}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}
}
这个组件在WebMvcConfigurationSupport的httpRequestHandlerAdapter方法注入。
小结
至此,我们用了6篇文章详细介绍了SpringMVC DispatcherServlet的注册、请求转发、消息转换等内容,DispatcherServlet的源码分析到此可以结束了。
相关文章:
SpringMVC DispatcherServlet源码(6) 完结 静态资源原理
阅读源码,分析静态资源处理器相关组件: 使用SimpleUrlHandlerMapping管理url -> 处理器映射关系spring mvc使用WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件DelegatingWebMvcConfiguration可以使用WebMvcConfigurer的配置静态资源url…...
2023年全国最新会计专业技术资格精选真题及答案9
百分百题库提供会计专业技术资格考试试题、会计考试预测题、会计专业技术资格考试真题、会计证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 四、材料题 1.某企业为增值税一般纳税人,2019年12月初“应付职工薪酬…...

Web3中文|把Web3装进口袋,Solana手机Saga有何魔力?
2月23日,Solana Web3手机Saga发布新的消息,将推出NFT铸造应用程序Minty Fresh。在Minty Fresh,用户仅需轻点并完成拍摄,就可以直接在手机中进行NFT铸造,并在几秒钟内将其转换为链上NFT,NFT还可以发布在 Ins…...

【配电网优化】基于串行和并行ADMM算法的配电网优化研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

数据结构初阶 -- 顺序表
数据结构初阶 链表的讲解 目录 一. 线性表 1.1 定义 1.2 特点 二. 顺序表 2.1 定义 2.2 代码 2.3 功能需求 2.4 静态顺序表的特点以及缺点 2.5 动态的顺序表 2.6 动态顺序表接口的实现 三. 代码 头文件 主文件 一. 线性表 1.1 定义 线性表(linear li…...

uniapp:3分钟搞定在线推送uni.createPushMessage,uni.onPushMessage
安卓端 在线推送功能演示: 1、dcloud后台申请开通uniPush dcloud后台 (1):找到我的应用 (2):点进去后,各平台信息,点击新增 (3):填…...

C/C++开发,无可避免的多线程(篇一).跨平台并行编程姗姗来迟
一、编译环境准备 在正式进入c/c多线程编程系列之前,先来搭建支持多线程编译的编译环境。 1.1 MinGW(win) 进入Downloads - MinGW-w64下载页面,选择MinGW-w64-builds跳转下载, 再次进行跳转: 然后进入下载页…...

如何把照片的底色修改为想要的颜色
如何给照片更换底色?其实有可以一键给照片更换底色的 APP ,但是几乎都要收费。如果想要免费的给照片更换底色的话,分享两种简单便捷的方法给你。掌握了这项技能,以后就不用店花钱处理啦!1、免费!线上快速 给…...

【高效办公】批量生成固定模板的文件夹名称
老师让你按照他的要求生成每位学生的文件夹,你是学委,让你马上完成该任务,但你又不想是手动一个一个码字,因此聪明的你就看到了本篇文章啦!!! 虽说一个人懒惰,并不是好的事情。 但这个似乎合情合理啊~ 然后,就动手想办法,一开始就真的打算码字了。。 思路 在实际开…...

redis的集群方式
1.主从复制 主从复制原理: 从服务器连接主服务器,发送SYNC命令; 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 主服务器BGSAVE执行完后,向所有从服务…...

温控负荷的需求响应潜力评估及其协同优化管理研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

模电学习9. MOS管使用入门
模电学习9. MOS管使用入门一、mos管理简介1. 简介2. mos管理的特点3. MOS管的工作状态(1)放大功能(2)截止区(3)饱和区3. Mos管的分类(1)按照工作模式分类:(2&…...
【算法】【数组与矩阵模块】正数组中累加和为给定值的最长子数组长度,空间复杂度O(1)解法
目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介绍 …...
3.1.2 创建表
文章目录1.创建表2.表创建基础3.表的主键4.使用null值5.使用AUTO_INCREMENT6.指定默认值7. 字段备注8.引擎类型9.外键1.创建表 表的创建一般有俩种方式,一种是使用交互式创建和管理表的工具,比如我们安装的MariaDB,另一种是使用MySQL 语句进…...

使用netlify实现自动化部署前端项目(无服务器版本)
介绍 本文以 github仓库进行介绍关联netlify的无服务前端自动化部署。用途:个人网站设计、小游戏等当然这只是让你入门~具体细节等待你自己去探索 实现 打开官方网站 如果没有注册过的账户,你需要使用 github 去进行登录。注册完成后会自动给你提示填…...
MATLAB点云数据处理(二十九):可视化点云之pcshow参数详解与快捷键操作
文章目录 1 pcshow简述2 最简单的pcshow3 带参数的pcshow3.1 点大小参数----MakerSize3.2 背景色参数----Background3.3 指定竖直轴参数----VerticalAxis3.4 指定垂直轴方向参数----VerticalAxisDir3.5 投影参数----Projection3.6 指定可视化平面参数----ViewPlane3.7 颜色渲染…...

顺序表——重置版
本期我们来实现数据结构的顺序表(这个之前写过一次,不过本期和之前可能会略有不同,但大体相同),大家可以看一下我们之前完成的顺序表 (6条消息) 顺序表及其多种接口的实现_顺序表类中实现接口方法_KLZUQ的博客-CSDN博客…...
PyQt5自然语言处理入门案例笔记
前言 最近想将自然语言处理的项目进行可视化,尽量还是使用回Python语言,因此打算用PyQt来实现相应的功能。 入门案例 一个简单的自然语言处理的demo,使用PyQt框架,该demo可以读取文本文件,对文件中的文本进行情感分…...
使用 CSS 替换表行颜色?
跳到主内容 我正在使用一个带有交替行颜色的表格。 tr.d0 td {background-color: #CC9999;color: black; } tr.d1 td {background-color: #9999CC;color: black; }<table><tr class"d0"><td>One</td><td>one</td></tr>&…...

智能家居控制系统
🥁作者: 华丞臧. 📕专栏:【项目经验】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...