8.8 Spring Boot靜態(tài)資源處理

8.8 Spring Boot靜態(tài)資源處理

當(dāng)使用Spring Boot來(lái)開(kāi)發(fā)一個(gè)完整的系統(tǒng)時(shí)芍阎,我們往往需要用到前端頁(yè)面,這就不可或缺地需要訪問(wèn)到靜態(tài)資源辖佣,比如圖片霹抛、css、js等文件卷谈。

Spring Boot使用 WebMvcAutoConfiguration 中的配置各種屬性, 默認(rèn)為我們提供了靜態(tài)資源處理杯拐。如果需要特殊處理的再通過(guò)配置進(jìn)行修改。

我們來(lái)看一下WebMvcAutoConfiguration類里面的默認(rèn)配置世蔗。

靜態(tài)資源默認(rèn)配置WebMvcAutoConfiguration

在WebMvcAutoConfiguration類里面有個(gè)addResourceHandlers方法:

        @Override
        public 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));
            }
        }

其中端逼,針對(duì)webjars做了默認(rèn)的處理:

registry.addResourceHandler("/webjars/**")
                                .addResourceLocations(
                                        "classpath:/META-INF/resources/webjars/")

其中,如果當(dāng)前的ResourceHandlerRegistry里面資源映射沒(méi)有 “/**”污淋, 則啟用SpringBoot內(nèi)置默認(rèn)的靜態(tài)資源處理:

String staticPathPattern = this.mvcProperties.getStaticPathPattern();//"/**"

            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(
                                        this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
            }

默認(rèn)的靜態(tài)資源映射路徑在ResourceProperties類里面顶滩,相關(guān)代碼如下:

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);
    }

    /**
     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
     * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
     */
    private String[] staticLocations = RESOURCE_LOCATIONS;

即默認(rèn)配置的 /** 映射到如下目錄:

classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/

綜上所述,Spring Boot 默認(rèn)配置為:

頁(yè)面請(qǐng)求路徑模式 靜態(tài)資源在工程的路徑 優(yōu)先級(jí)
/** classpath:/META-INF/resources/ 第1優(yōu)先
/** classpath:/resources/ 第2優(yōu)先
/** classpath:/META-INF/resources/ 第3優(yōu)先
/** classpath:/static/ 第4優(yōu)先
/webjars/** classpath:/META-INF/resources/webjars/ -

自定義靜態(tài)資源映射

在SpringBoot集成Swagger的時(shí)候寸爆,我們需要增加swagger-ui.html映射到classpath:/META-INF/resources/礁鲁,我們自定義配置類浊吏,繼承WebMvcConfigurerAdapter,然后重寫(xiě)addResourceHandlers方法:

@Configuration
//@EnableWebMvc //這個(gè)注解會(huì)覆蓋掉SpringBoot的默認(rèn)的靜態(tài)資源映射配置
class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    void addResourceHandlers(ResourceHandlerRegistry registry) {
        
        //Swagger ui Mapping
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/")

    }

}

如果想要自己完全控制WebMVC(完全覆蓋SpringBoot默認(rèn)配置)救氯,就需要在@Configuration注解的配置類上增加@EnableWebMvc,當(dāng)增加@EnableWebMvc注解以后歌憨,WebMvcAutoConfiguration中配置就不會(huì)生效着憨,會(huì)自動(dòng)覆蓋了官方給出的/static, /public, META-INF/resources, /resources等存放靜態(tài)資源的目錄。而將靜態(tài)資源定位于src/main/webapp务嫡。

在spring-boot-features.adoc中指出甲抖,如果你的應(yīng)用要打成jar形式來(lái)運(yùn)行的話,不要把靜態(tài)資源放到src/main/webapp目錄心铃,雖然這是標(biāo)準(zhǔn)目錄准谚,但是僅在打war包的時(shí)候起作用。因?yàn)榇蠖鄶?shù)的構(gòu)建工具在打jar包的時(shí)候去扣,會(huì)默認(rèn)忽略此目錄:

TIP: Do not use the src/main/webapp directory if your application will be packaged as a
jar. Although this directory is a common standard, it will only work with war packaging
and it will be silently ignored by most build tools if you generate a jar.

當(dāng)需要重新定義好資源所在目錄時(shí)柱衔,則需要主動(dòng)添加上述的那個(gè)配置類,來(lái)Override addResourceHandlers方法愉棱。你需要自己來(lái)配置需要的每一項(xiàng)唆铐。

這種方式會(huì)在默認(rèn)的基礎(chǔ)上增加

swagger-ui.html映射到classpath:/META-INF/resources/

不會(huì)影響默認(rèn)的方式,可以同時(shí)使用奔滑。

前端資源的引用方法

在index.ftl中該如何引用上面的靜態(tài)資源呢艾岂?
推薦默認(rèn)的寫(xiě)法如下:

<link rel="stylesheet" type="text/css" href="/css/index.css">
<script type="text/javascript" src="/js/index.js"></script>

注意:默認(rèn)配置的/**映射到/static(或/public ,/resources朋其,/META-INF/resources)

當(dāng)請(qǐng)求/css/index.css的時(shí)候王浴,Spring MVC 會(huì)在/static/目錄下面找到。

如果配置為/static/js/index.js

<script src="${request.contextPath}/static/js/index.js"></script>

那么上面配置的幾個(gè)目錄下面都沒(méi)有/static目錄梅猿,因此會(huì)找不到資源文件氓辣。

這個(gè)時(shí)候,就需要另外添加自定義的映射了:

registry.addResourceHandler("/static/**")
               .addResourceLocations("classpath:/static/")

所以袱蚓,當(dāng)我們使用SpringBoot默認(rèn)靜態(tài)資源配置的時(shí)候筛婉,寫(xiě)靜態(tài)資源位置不要帶上映射的目錄名(如/static/,/public/ 癞松,/resources/爽撒,/META-INF/resources/)。

使用WebJars

Spring Boot 在支持 Spring MVC的靜態(tài)資源處理的特性的同時(shí), 允許使用jar包版本的靜態(tài)資源和使用版本無(wú)關(guān)的URL的靜態(tài)資源的引用响蓉。它就是Webjars[1]硕勿。

例如,使用jQuery枫甲,添加依賴:

compile group: 'org.webjars.bower', name: 'jquery', version: '3.2.1'

然后源武,在前端html代碼中扼褪,就可以直接如下使用:

<script type="text/javascript" src="/webjars/jquery/3.2.1/jquery.js"></script>

你可能注意到href中的1.11.3版本號(hào)了,如果僅僅這么使用粱栖,那么當(dāng)我們切換版本號(hào)的時(shí)候還要手動(dòng)修改href话浇,比較麻煩。我們完全可以約定一套目錄規(guī)則闹究,把后端webjars的依賴版本幔崖,直接傳遞到后端。而負(fù)責(zé)完成維護(hù)管理這套目錄規(guī)則的人就是webjars-locator渣淤。webjars-locator通過(guò)在classpath中尋找需要加載的靜態(tài)資源赏寇,然后引入前端頁(yè)面。查找路徑的邏輯的方法是WebJarAssetLocator類里的getFullPath方法价认。

我們要想使用webjars-locator嗅定,先要添加依賴:

    //Group: org.webjars.bower
    // https://mvnrepository.com/artifact/org.webjars/webjars-locator
    compile group: 'org.webjars', name: 'webjars-locator', version: '0.32'

然后,增加一個(gè)WebJarController:

package com.easy.springboot.h5perf.controller

import org.springframework.core.io.ClassPathResource
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.servlet.HandlerMapping
import org.webjars.WebJarAssetLocator

import javax.servlet.http.HttpServletRequest

/**
 * Created by jack on 2017/4/22.
 */
@Controller
class WebJarsController {
    WebJarAssetLocator assetLocator = new WebJarAssetLocator();

    @ResponseBody
    @RequestMapping("/webjarslocator/{webjar}/**")
    def ResponseEntity<?> locateWebjarAsset(@PathVariable String webjar, HttpServletRequest request) {
        try {
            String mvcPrefix = "/webjarslocator/" + webjar + "/"; // This prefix must match the mapping path!
            String mvcPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
            String fullPath = assetLocator.getFullPath(webjar, mvcPath.substring(mvcPrefix.length()));
            return new ResponseEntity<>(new ClassPathResource(fullPath), HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }
}


然后用踩,前端代碼使用方式如下:

<link href="${request.contextPath}/webjarslocator/datatables/jquery.dataTables.min.css" rel="stylesheet" type="text/css">
<link href="${request.contextPath}/webjarslocator/tether/tether.min.css" rel="stylesheet" type="text/css">
<link href="${request.contextPath}/css/index.css" rel="stylesheet" type="text/css">
<link href="${request.contextPath}/css/report.css" rel="stylesheet" type="text/css">

<script src="${request.contextPath}/webjarslocator/jquery/jquery.min.js"></script>
<script src="${request.contextPath}/js/index.js"></script>

<script src="${request.contextPath}/webjarslocator/tether/tether.min.js"></script>
<script src="${request.contextPath}/webjarslocator/bootstrap/bootstrap.min.js"></script>
<script src="${request.contextPath}/webjarslocator/datatables/jquery.dataTables.min.js"></script>

這里是freemarker的模板view代碼示例渠退。其中,request對(duì)象是內(nèi)置對(duì)象脐彩。在application.yml配置如下:

spring:
    # 在freemarker獲取request對(duì)象
    freemarker:
        request-context-attribute: request

注意:這里不需要在寫(xiě)版本號(hào)了智什,但是注意寫(xiě)url的時(shí)候,只是在原來(lái)url基礎(chǔ)上去掉了版本號(hào)丁屎,其他的都不能少荠锭!

靜態(tài)資源動(dòng)態(tài)版本

當(dāng)我們資源內(nèi)容發(fā)生變化時(shí),由于瀏覽器緩存晨川,用戶本地的靜態(tài)資源還是舊的資源证九,為了防止這種情況導(dǎo)致的問(wèn)題,我們?cè)谡?qǐng)求url的時(shí)候加個(gè)版本號(hào)共虑。

版本號(hào)如:

<script type="text/javascript" src="/js/index.js?v=1.0.1"></script>

這個(gè)版本號(hào)1.0.1愧怜,可以由后端代碼傳到前端頁(yè)面${version}。

小結(jié)

本章節(jié)主要探討了Spring Boot 靜態(tài)資源處理的內(nèi)容妈拌。當(dāng)我們?cè)陂_(kāi)發(fā)中拥坛,遵循SpringBoot的默認(rèn)配置,可以大大減少了我們靜態(tài)資源處理的工作尘分。

本章節(jié)完整的工程代碼:

https://github.com/EasySpringBoot/h5perf

參考資料:

1.WebJars:http://www.webjars.org/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猜惋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子培愁,更是在濱河造成了極大的恐慌著摔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件定续,死亡現(xiàn)場(chǎng)離奇詭異谍咆,居然都是意外死亡禾锤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)摹察,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恩掷,“玉大人,你說(shuō)我怎么就攤上這事供嚎』颇铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵查坪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我宁炫,道長(zhǎng)偿曙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任羔巢,我火速辦了婚禮望忆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竿秆。我一直安慰自己启摄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布幽钢。 她就那樣靜靜地躺著歉备,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匪燕。 梳的紋絲不亂的頭發(fā)上蕾羊,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音帽驯,去河邊找鬼龟再。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尼变,可吹牛的內(nèi)容都是我干的利凑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嫌术,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哀澈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起度气,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤日丹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蚯嫌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哲虾,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丙躏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了束凑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晒旅。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汪诉,靈堂內(nèi)的尸體忽然破棺而出废恋,到底是詐尸還是另有隱情,我是刑警寧澤扒寄,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布鱼鼓,位于F島的核電站,受9級(jí)特大地震影響该编,放射性物質(zhì)發(fā)生泄漏迄本。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一课竣、第九天 我趴在偏房一處隱蔽的房頂上張望嘉赎。 院中可真熱鬧,春花似錦于樟、人聲如沸公条。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)靶橱。三九已至,卻和暖如春路捧,著一層夾襖步出監(jiān)牢的瞬間抓韩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工鬓长, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谒拴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓涉波,卻偏偏與公主長(zhǎng)得像英上,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啤覆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理苍日,服務(wù)發(fā)現(xiàn),斷路器窗声,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評(píng)論 6 342
  • 要加“m”說(shuō)明是MB相恃,否則就是KB了. -Xms:初始值 -Xmx:最大值 -Xmn:最小值 java -Xms8...
    dadong0505閱讀 4,811評(píng)論 0 53
  • SpringMVC原理分析 Spring Boot學(xué)習(xí) 5、Hello World探究 1笨觅、POM文件 1拦耐、父項(xiàng)目...
    jack_jerry閱讀 1,269評(píng)論 0 1
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架耕腾,建立于...
    Hsinwong閱讀 22,353評(píng)論 1 92