SpringBoot靜態資源配置原理(源碼分析)
前言:
我們都知道,SpringBoot啟動會默認加載很多xxxAutoConfiguration類(自動配置類)
其中SpringMVC的大都數功能都集中在WebMvcAutoConfiguration類中,根據條件ConditionalOnxxx註冊類對象;WebMvcAutoConfiguration滿足以下ConditionalOnxxx條件,類是生效的,並把其對象註冊到容器中。
那WebMvcAutoConfiguration生效給容器中配置瞭什麼呢?
WebMvcAutoConfigurationAdapter靜態內部類
一.配置文件前綴
我們來看WebMvcAutoConfiguration類中的WebMvcAutoConfigurationAdapter靜態內部類:
這是一個配置類,配置文件的屬性和xxx進行瞭綁定。
再看@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})
我們來看當中的WebMvcProperties、ResourceProperties和WebProperties的字節碼文件
分別點進這三個類的字節碼文件中:
可以看到WebMvcProperties它是與配置文件前綴spring.mvc相關聯的。
ResourceProperties它是與配置文件前綴spring.resources相關聯。
WebProperties它是與配置文件前綴spring.web相關聯。
二.隻有一個有參構造器
WebMvcAutoConfigurationAdapter靜態內部配置類隻有一個有參數的構造器,那它會帶來什麼特性呢?
它的有參構造器中所有參數的值都會從容器中確定
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) { this.resourceProperties = (Resources)(resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources()); this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConvertersProvider = messageConvertersProvider; this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this.dispatcherServletPath = dispatcherServletPath; this.servletRegistrations = servletRegistrations; this.mvcProperties.checkConfiguration(); }
我們來看下它的參數:
- 第一個參數是ResourceProperties resourceProperties 就是我們上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中註冊開啟的第二個類,獲取和spring.resources綁定的所有的值的對象
- 第二個參數是WebProperties webProperties 就是我們上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中註冊開啟的第三個類,獲取和spring.web綁定的所有的值的對象
- 第三個參數是WebMvcProperties mvcProperties 就是我們上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中註冊開啟的第一個類,獲取和spring.mvc綁定的所有的值的對象
- 第四個參數是ListableBeanFactory beanFactory ,這個是Spring的beanFactory,也就是我們的容器。
- 第五個參數是ObjectProvider messageConvertersProvider,找到所有的HttpMessageConverters
- 第六個參數是ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,找到資源處理器的自定義器
- 第七個參數是ObjectProvider dispatcherServletPath,相當與找dispatcherServlet能處理的路徑
- 第八個參數是ObjectProvider<ServletRegistrationBean<?>> servletRegistrations ,給應用註冊原生的Servlet、Filter等等
構造器初始化後,我們已經把所有的東西從容器中拿到瞭
三.源碼分析addResourceHandlers方法
所有的資源處理默認規則都在addResourceHandlers方法中,如下:
public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); } else { Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); if (!registry.hasMappingForPattern("/webjars/**")) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified())); } String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified())); } } }
1.禁用掉靜態資源的路徑映射
我們打上斷點看它的默認規則是怎麼起作用的,首先調用resourcePropertoes的isAddMappings()方法:
判斷this.resourcePropertoes的isAddMappings()方法是不是不為true,
- this.resourcePropertoes我們剛才在2中講構造器時講到的ResourceProperties resourceProperties 就是我們上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中註冊開啟的第二個類,獲取和spring.resources綁定的所有的值的對象
- isAddMappings()方法返回的是this.addMappings的值,如下:
也就是說我們可以通過設置addMappings的值是false還是true來讓這個if語句是否執行
我們可以在配置文件中進行設置:
默認它是true,如果是false,那麼他就進入if語句中,執行logger.debug("Default resource handling disabled");
後結束該方法,else中的所有配置都不生效
else中的什麼配置/webjars/**
去哪找等等一些規則都不生效瞭。
也就是說我們通過設置add-mappings: false
來禁用掉瞭靜態資源的路徑映射。
禁用後所有的靜態資源都訪問不瞭瞭。
addMappings的值如果是true,那麼他就不會進入if語句中,而是進入到else語句中,那麼else語句的內容都得到瞭執行,下面我們看它是怎麼配置靜態資規則的。
2.源碼分析webjars的底層規則
進入到else語句中,第一行是Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
,它從resourceProperties裡面獲取到關於緩存的相關值。我們在yaml配置文件中配置一下這個值:
緩存時間是以秒為單位的,如下:
意思就是我們所有的靜態資源默認可以緩存存儲多少秒
我們debug接著往下走,看到cachePeriod中取到瞭剛剛yaml中設置的6666,以後我們的瀏覽器就會把我們的靜態資源緩存6666秒:
debug接著往下走,我們到瞭註冊"/webjars/**"
這個規則的地方:
if (!registry.hasMappingForPattern("/webjars/**")) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified())); }
也就是說我們訪問/webjars
下面的所有請求都找我們的classpath:/META-INF/resources/webjars/
路徑,其中還設置瞭其靜態資源的緩存時間為6666秒。
拿jquery來舉例,為什麼我們導入jquery之後,我們隻需要訪問/webjars/jquery/3.5.1/jquery.js就能夠訪問到/META-INF/resources/webjars/jquery/3.5.1/jquery.js,如下:
其緩存時間也可以在瀏覽器中看到為6666秒:
3.源碼分析默認靜態資源路徑的底層規則
我們在else裡面接著往下debug,接著我們用mvcProperties屬性調用其getStaticPathPattern()方法
- this.mvcProperties我們剛才在2中講構造器時講到的WebMvcProperties mvcProperties 就是我們上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中註冊開啟的第一個類,獲取和spring.mvc綁定的所有的值的對象
- getStaticPathPattern()方法,這個方法返回的是staticPathPattern的值,如下:
staticPathPattern的這個值可以在我們的配置文件中進行配置,它的默認值是/**
,如下:
我們也可以把前綴配置成/resource/**
,如下:
debug接著往下走,接下來調用的方法與上面的webjars
是一樣的方法,隻不過參數有所不同:
接下來我們具體看代碼:
String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified())); }
把剛剛的前綴staticPathPattern得到後作為實參傳入hasMappingForPattern方法中,註冊前綴
這個規則,剛剛我們在yaml中設置瞭前綴為/resource/**
,也就是說我們訪問/resource/**
下面的所有請求都找我們的this.resourceProperties.getStaticLocations()
路徑,其中也設置瞭其靜態資源的緩存時間為6666秒。
this.resourceProperties.getStaticLocations()
方法返回的值是什麼呢?我們點進去看一下:
this.resourceProperties.getStaticLocations()
返回的是this.staticLocations,這個staticLocations定義如下:
可以看到它是一個字符串數組,在無參構造器中進行瞭初始化,初始化的值是CLASSPATH_RESOURCE_LOCATIONS常量,常量的值為:
“classpath:/META-INF/resources/”, “classpath:/resources/”, “classpath:/static/”, “classpath:/public/“。這就解釋瞭靜態資源路徑為什麼默認為這四個路徑。
看到這裡你有沒有恍然大悟,
到此這篇關於SpringBoot靜態資源配置原理(源碼分析)的文章就介紹到這瞭,更多相關SpringBoot靜態資源配置內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 聊聊springboot靜態資源加載的規則
- springboot中的靜態資源加載順序優先級
- SpringBoot+Thymeleaf靜態資源的映射規則說明
- SpringBoot通過源碼探究靜態資源的映射規則實現
- SpringBoot 靜態資源導入及首頁設置問題