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.pswp.cn/news/389637.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389637.shtml
英文地址,請注明出處:http://en.pswp.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…