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刷题网站…...
Uvicorn与AWS CloudFormation StackSets:多账户部署的终极指南
Uvicorn与AWS CloudFormation StackSets:多账户部署的终极指南 【免费下载链接】uvicorn An ASGI web server, for Python. 🦄 项目地址: https://gitcode.com/GitHub_Trending/uv/uvicorn Uvicorn 作为一款高性能的 ASGI 服务器,为 P…...
OpenClaw配置备份:Qwen3.5-9B模型参数迁移与快速恢复方案
OpenClaw配置备份:Qwen3.5-9B模型参数迁移与快速恢复方案 1. 为什么需要系统化备份OpenClaw配置 上周我的开发机SSD突然故障,导致整个系统需要重装。当我重新部署OpenClaw时,突然意识到一个严重问题:过去三个月精心调试的模型参…...
SkeyeVSS中SSE(Server-Sent Events)架构设计
本文说明 core/app/sev/vss 信令服务内 SSE 长连接 的实现方式:独立 HTTP 服务、/events 入口、按 type 路由到不同 Logic,以及 messageChan → 文本帧 → Flush 的推送模型。可与《SkeyeVSS中HTTP架构设计》《skeyeVSS中WebSocket架构设计》对照阅读。 …...
OpenClaw调试技巧:百川2-13B任务失败时的6种排查方法
OpenClaw调试技巧:百川2-13B任务失败时的6种排查方法 1. 为什么需要专门的调试方法? 上周我让OpenClaw自动整理一批会议录音转文字稿,结果凌晨3点收到飞书报警——任务卡在"正在分析关键内容"阶段。第二天检查发现,百…...
解锁Intel RealSense三维点云生成:3大突破点与实战秘籍
解锁Intel RealSense三维点云生成:3大突破点与实战秘籍 【免费下载链接】librealsense Intel RealSense™ SDK 项目地址: https://gitcode.com/GitHub_Trending/li/librealsense 在工业检测、机器人导航和增强现实等领域,三维数据获取一直是技术落…...
独立转向轮式机器人避障轨迹规划策略:应对未知地形与突发空中障碍
独立转向轮式机器人避障轨迹规划策略 (应对未知地形和突发空中障碍) 1、改进动态窗口法(采样策略和评价策略) 2、基于模糊规则的自适应权重策略 (程序完整,注释详细,可供相关方向研究生借鉴参考…...
OpenClaw/阿里copaw/阿里QoderWork/腾讯Qclaw/腾讯workbuddy综合对比
1、功能介绍 核心能力:自然语言交互、本地文件操作、代码执行 支持模型:Qwen、Deepseek、OpenAI 等主流厂家模型均支持(硬件条件允许,也可通过ollama连接本地模型) 机器人助手:飞书、企业微信、QQ等创建…...
Docker 学习之路-从入门到放弃-Jenkins:4
Jenkins 打开 ✅ 如图已经完全成功安装并初始化Jenkins了!从图1可以确认:能正常访问Jenkins Web管理界面、登录成功核心功能入口(Create a job/Manage Jenkins等)正常显示构建执行器(Build Executor Status)…...
Windows下Python虚拟环境激活报错?一招搞定PowerShell脚本执行权限问题
Windows下Python虚拟环境激活报错?一招搞定PowerShell脚本执行权限问题 在Windows平台上使用Python虚拟环境时,许多开发者都遇到过这样的报错信息:"无法加载文件 venv\Scripts\Activate.ps1,因为在此系统上禁止运行脚本"…...
IPTV抓包工具合集:Wireshark、parse_cap_channels_v2、IPTV全能工具箱
分享一个刚刚大佬那里转存过来的IPTV工具箱v5.2版本。先叠个甲,这仅仅是一个单纯的源检测和管理工具分享,不包含任何IPTV源地址,也不涉及任何违规教程。如果版主认为违规请直接删帖。 这个软件主打一个省心。不需要你自己有服务器,…...
