Spring Boot 静态资源访问原理解析

一、前言

  springboot配置静态资源方式是多种多样,接下来我会介绍其中几种方式,并解析一下其中的原理。

二、使用properties属性进行配置

  应该说 spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的,如果不明白其中的原理,总会出现资源404的情况。首先收一下spring.mvc.static-path-pattern代表的是一个Ant Path路径,例如resources/**,表示当你的路径中存在resources/**的时候才会处理请求。比如我们访问“http://localhost:8080/resources/xxx.js”时,很显然,springboot逻辑中会根据模式匹配对url进行匹配,匹配命中后,是如何再定位到具体的资源的呢?这时候spring.resources.static-locations的配置就起作用了。

  忘记说了,在springboot中spring.mvc.static-path-pattern的默认值是/**,spring.resources.static-locations的默认值是classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources,servlet context:/,springboot中相关的ResourceHttpRequestHandler就会去spring.resources.static-locations配置的所有路径中寻找资源文件。

  所以我之前才说spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的。

三、springboot中默认对静态资源的处理

  调试过程中,通过查看 org.springframework.web.servlet.DispatcherServlet中的handlerMappings变量,我们发现有一个很显眼的 resourceHandlerMapping ,这个是springboot为我们提供的一个默认的静态资源handler,通过全文搜索发现出现在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport这个类中,也就是这个类包含了@EnableWebMvc注解中的大多数功能,更多的扩展功能请参考org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration。

  resourceHandlerMapping 的定义如下。

/*** Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped* resource handlers. To configure resource handling, override* {@link #addResourceHandlers}.*/
@Bean
public HandlerMapping resourceHandlerMapping() {ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,this.servletContext, mvcContentNegotiationManager());addResourceHandlers(registry);AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();if (handlerMapping != null) {handlerMapping.setPathMatcher(mvcPathMatcher());handlerMapping.setUrlPathHelper(mvcUrlPathHelper());handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));handlerMapping.setCorsConfigurations(getCorsConfigurations());}else {handlerMapping = new EmptyHandlerMapping();}return handlerMapping;
}    

  请大家先记住ResourceHandlerRegistry这个类。

    首先看一下addResourceHandlers(registry);这个方法,父类DelegatingWebMvcConfiguration做了实现,如下。

 
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {this.configurers.addResourceHandlers(registry);
}

  其中WebMvcConfigurerComposite是操作了WebMvcConfigurer类型的对象的集合。在org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration这个springmvc的自动配置类中,有一个WebMvcConfigurer的实现类,如下。

// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
// on the classpath
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {...@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");return;}Integer cachePeriod = this.resourceProperties.getCachePeriod();if (!registry.hasMappingForPattern("/webjars/**")) {customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(cachePeriod));} String staticPathPattern = this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern).addResourceLocations(this.resourceProperties.getStaticLocations()).setCachePeriod(cachePeriod));}}...
}

  上面的addResourceHandlers方法中,增加了默认的mapping pattern = /webjars/** ,默认的resource location是classpath:/META-INF/resources/webjars/。正是这里的配置,我们在集成swagger的时候,就可以正常访问到swagger webjars中的js文件了。其中红色的代码部分就是用户可以自定义的默认静态资源访问方式,并通过ResourceHandlerRegistry对象进行注册。接着看一下mvcProperties和resourceProperties对应的类吧。

@ConfigurationProperties("spring.mvc")
public class WebMvcProperties {.../*** Path pattern used for static resources.*/private String staticPathPattern = "/**";...
}

  WebMvcProperties类中的staticPathPattern field 对应了spring.mvc.static-path-pattern这个属性,可以看到默认值是 "/**"。

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {.....private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/" };private static final String[] RESOURCE_LOCATIONS;static {RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length+ SERVLET_RESOURCE_LOCATIONS.length];System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,SERVLET_RESOURCE_LOCATIONS.length);System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);}private String[] staticLocations = RESOURCE_LOCATIONS;......
}

  ResourceProperties中staticLocations field 对应了 spring.resources.static-locations 这个属性。可以看到默认值是classpath:[/META-INF/resources/, /resources/, /static/, /public/], servlet context:/

四、静态资源的Bean配置

  在了解了springboot默认资源的配置的原理(即 spring.mvc.static-path-pattern 和 spring.resources.static-locations),我们可以增加一个WebMvcConfigurer类型的bean,来添加静态资源的访问方式,还记得上面说的“请记住ResourceHandlerRegistry这个类“,下面就用到了哦。

@Configuration
public class ResourceWebMvcConfigurer extends WebMvcConfigurerAdapter {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/public-resources/").setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());}
}

  那么当访问路径中包含"resources/**"的时候,resource handler就会去classpath:/public-resources目录下寻找了。

五、静态资源的查找

  参考 org.springframework.web.servlet.resource.ResourceHttpRequestHandler,ResourceHttpRequestHandler中通过org.springframework.web.servlet.resource.PathResourceResolver进行查找。

  举个例子,下图是springboot打包之后的目录结构,现在想要通过url访问application.properties文件,springboot默认的静态文件配置可以吗?当然需要用事实来说话了。

   

   我们已经知道,默认的resource locations中有个 servlet-context:/,访问你的url是http://localhost:8080/工程名/application.properties,调试一下PathResourceResolver,结果如下。

  

  

  发现servlet-context的根路径如上图所示,查看一下这个路径对应的目录,发现什么都没有,所以很显然无法找到我们要找的文件了。毕竟一般使用springboot都是jar项目,servlet-context path下没有用户自定义的资源。

 六、其他方式

  在Servlet3协议规范中,包含在JAR文件/META-INFO/resources/路径下的资源可以直接访问了。如果将springboot项目打包成war包,可以配置一个默认的servlet。在WebMvcConfigurationSupport中已经定义好了,不过默认是一个EmptyHandlerMapping。

/*** Return a handler mapping ordered at Integer.MAX_VALUE with a mapped* default servlet handler. To configure "default" Servlet handling,* override {@link #configureDefaultServletHandling}.*/
@Bean
public HandlerMapping defaultServletHandlerMapping() {DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);configureDefaultServletHandling(configurer);AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping();handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();return handlerMapping;
}

  可以通过自定义一个WebMvcConfigurer类型的bean,改写configureDefaultServletHandling 方法,如下。

@Configuration
public class MyWebConfigurer extends WebMvcConfigurerAdapter {@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}
}

  这样就设置了一个默认的servlet,在加载静态资源的时候就会按照servelt方式去加载了。

 

  就先分享这么多了,更多分享请关注我们的技术公众号吧!!!

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

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

相关文章

深挖“窄带高清”的实现原理

过去几年,又拍云一直在点播、直播等视频应用方面潜心钻研,取得了不俗的成果。我们结合点播、直播、短视频等业务中的用户场景,推出了“省带宽、压成本”系列文章,从编码技术、网络架构等角度出发,结合又拍云的产品成果…

学习总结5 - bootstrap学习记录1__安装

1.bootstrap是什么? 简洁、直观、强悍的前端开发框架,说白了就是给后端二把刀开发网页用的,让web开发更迅速、简单。 复制代码 2.如何使用? 如图所示到bootstrap中文网进行下载 复制代码 下载完成之后,如图所示&#x…

519. 随机翻转矩阵

519. 随机翻转矩阵 给你一个 m x n 的二元矩阵 matrix ,且所有值被初始化为 0 。请你设计一个算法,随机选取一个满足 matrix[i][j] 0 的下标 (i, j) ,并将它的值变为 1 。所有满足 matrix[i][j] 0 的下标 (i, j) 被选取的概率应当均等。 …

模型的搜索和优化方法综述:

一、常用的优化方法: 1.爬山 2.最陡峭下降 3.期望最大值 二、常用的搜索方法: 1.贪婪搜索 2.分支界定 3.宽度(深度)优先遍历转载于:https://www.cnblogs.com/xyp666/p/9042143.html

Redis 服务安装

下载 客户端可视化工具: RedisDesktopManager redis官网下载: http://redis.io/download windos服务安装 windows服务安装/卸载下载文件并解压使用 管理员身份 运行命令行并且切换到解压目录执行 redis-service --service-install windowsR 打开运行窗口, 输入 services.msc 查…

熊猫数据集_对熊猫数据框使用逻辑比较

熊猫数据集P (tPYTHON) Logical comparisons are used everywhere.逻辑比较随处可见 。 The Pandas library gives you a lot of different ways that you can compare a DataFrame or Series to other Pandas objects, lists, scalar values, and more. The traditional comp…

初级功能笔试题-1

给我徒弟整理的一些理论性的笔试题,不喜勿喷。(所以没有答案哈) 1、测试人员返测缺陷时,如果缺陷未修复,把缺陷的状态置为下列什么状态()。 2、当验证被测系统的主要业务流程和功能是否实现时&a…

ansbile--playbook剧本案例

个人博客转至: www.zhangshoufu.com 通过ansible批量管理三台服务器,使三台服务器实现备份,web01、nfs、backup,把web和nfs上的重要文件被分到backup上,主机ip地址分配如下 CharacterIP地址IP地址主机名Rsync--server1…

5938. 找出数组排序后的目标下标

5938. 找出数组排序后的目标下标 给你一个下标从 0 开始的整数数组 nums 以及一个目标元素 target 。 目标下标 是一个满足 nums[i] target 的下标 i 。 将 nums 按 非递减 顺序排序后,返回由 nums 中目标下标组成的列表。如果不存在目标下标,返回一…

决策树之前要不要处理缺失值_不要使用这样的决策树

决策树之前要不要处理缺失值As one of the most popular classic machine learning algorithm, the Decision Tree is much more intuitive than the others for its explainability. In one of my previous article, I have introduced the basic idea and mechanism of a Dec…

说说 C 语言中的变量与算术表达式

我们先来写一个程序&#xff0c;打印英里与公里之间的对应关系表。公式&#xff1a;1 mile1.61 km 程序如下&#xff1a; #include <stdio.h>/* print Mile to Kilometre table*/ main() {float mile, kilometre;int lower 0;//lower limitint upper 1000;//upper limi…

gl3520 gl3510_带有gl gl本机的跨平台地理空间可视化

gl3520 gl3510Editor’s note: Today’s post is by Ib Green, CTO, and Ilija Puaca, Founding Engineer, both at Unfolded, an “open core” company that builds products and services on the open source deck.gl / vis.gl technology stack, and is also a major contr…

uiautomator +python 安卓UI自动化尝试

使用方法基本说明&#xff1a;https://www.cnblogs.com/mliangchen/p/5114149.html&#xff0c;https://blog.csdn.net/Eugene_3972/article/details/76629066 环境准备&#xff1a;https://www.cnblogs.com/keeptheminutes/p/7083816.html 简单实例 1.自动化安装与卸载 &#…

5922. 统计出现过一次的公共字符串

5922. 统计出现过一次的公共字符串 给你两个字符串数组 words1 和 words2 &#xff0c;请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。 示例 1&#xff1a;输入&#xff1a;words1 ["leetcode","is","amazing","as",&…

Python+Appium寻找蓝牙/wifi匹配

前言&#xff1a; 此篇是介绍怎么去寻找蓝牙&#xff0c;进行匹配。主要2个问题点&#xff1a; 1.在不同环境下&#xff0c;搜索到的蓝牙数量有变 2.在不同环境下&#xff0c;搜索到的蓝牙排序会变 简单思路&#xff1a; 将搜索出来的蓝牙名字添加到一个list去&#xff0c;然后…

power bi中的切片器_在Power Bi中显示选定的切片器

power bi中的切片器Just recently, while presenting my session: “Magnificent 7 — Simple tricks to boost your Power BI Development” at the New Stars of Data conference, one of the questions I’ve received was:就在最近&#xff0c;在“新数据之星”会议上介绍我…

字符串匹配 sunday算法

#include"iostream" #include"string.h" using namespace std;//BF算法 int strfind(char *s1,char *s2,int pos){int len1 strlen(s1);int len2 strlen(s2);int i pos - 1,j 0;while(j < len2){if(s1[i j] s2[j]){j;}else{i;j 0;}}if(j len2){…

5939. 半径为 k 的子数组平均值

5939. 半径为 k 的子数组平均值 给你一个下标从 0 开始的数组 nums &#xff0c;数组中有 n 个整数&#xff0c;另给你一个整数 k 。 半径为 k 的子数组平均值 是指&#xff1a;nums 中一个以下标 i 为 中心 且 半径 为 k 的子数组中所有元素的平均值&#xff0c;即下标在 i …

Adobe After Effects CS6 操作记录

安装 After Effects CS6 在Mac OS 10.12.5 上无法直接安装, 需要浏览到安装的执行文件后才能进行 https://helpx.adobe.com/creative-cloud/kb/install-creative-suite-mac-os-sierra.html , 但是即使安装成功, 也不能正常启动, 会报"You can’t use this version of the …

数据库逻辑删除的sql语句_通过数据库的眼睛查询sql的逻辑流程

数据库逻辑删除的sql语句Structured Query Language (SQL) is famously known as the romance language of data. Even thinking of extracting the single correct answer from terabytes of relational data seems a little overwhelming. So understanding the logical flow…