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刷题网站…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
