Spring Boot靜態資源處理

<div class="article"> <h1 class="title">Spring Boot靜態資源處理</h1>javascript

<!-- 文章內容 -->
    <div data-note-content="" class="show-content">
      <div class="show-content-free">
        <h1>8.8 Spring Boot靜態資源處理</h1>

<p>當使用Spring Boot來開發一個完整的系統時,咱們每每須要用到前端頁面,這就不可或缺地須要訪問到靜態資源,好比圖片、css、js等文件。</p> <p>Spring Boot使用 WebMvcAutoConfiguration 中的配置各類屬性, 默認爲咱們提供了靜態資源處理。若是須要特殊處理的再經過配置進行修改。</p> <p>咱們來看一下WebMvcAutoConfiguration類裏面的默認配置。</p> <h2>靜態資源默認配置WebMvcAutoConfiguration</h2> <p>在WebMvcAutoConfiguration類裏面有個addResourceHandlers方法:</p> <pre class="hljs java"><code class="java"> <span class="hljs-meta">@Override</span></br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addResourceHandlers</span><span class="hljs-params">(ResourceHandlerRegistry registry)</span> </span>{</br> <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.resourceProperties.isAddMappings()) {</br> logger.debug(<span class="hljs-string">"Default resource handling disabled"</span>);</br> <span class="hljs-keyword">return</span>;</br> }</br> Integer cachePeriod = <span class="hljs-keyword">this</span>.resourceProperties.getCachePeriod();</br> <span class="hljs-keyword">if</span> (!registry.hasMappingForPattern(<span class="hljs-string">"/webjars/**"</span>)) {</br> customizeResourceHandlerRegistration(</br> registry.addResourceHandler(<span class="hljs-string">"/webjars/**"</span>)</br> .addResourceLocations(</br> <span class="hljs-string">"classpath:/META-INF/resources/webjars/"</span>)</br> .setCachePeriod(cachePeriod));</br> }</br> String staticPathPattern = <span class="hljs-keyword">this</span>.mvcProperties.getStaticPathPattern();</br> <span class="hljs-keyword">if</span> (!registry.hasMappingForPattern(staticPathPattern)) {</br> customizeResourceHandlerRegistration(</br> registry.addResourceHandler(staticPathPattern)</br> .addResourceLocations(</br> <span class="hljs-keyword">this</span>.resourceProperties.getStaticLocations())</br> .setCachePeriod(cachePeriod));</br> }</br> }</br> </code></pre> <p>其中,針對webjars作了默認的處理:</p> <pre class="hljs bash"><code class="bash">registry.addResourceHandler(<span class="hljs-string">"/webjars/**"</span>)</br> .addResourceLocations(</br> <span class="hljs-string">"classpath:/META-INF/resources/webjars/"</span>)</br> </code></pre> <p>其中,若是當前的ResourceHandlerRegistry裏面資源映射沒有 「/**」, 則啓用SpringBoot內置默認的靜態資源處理:</p> <pre class="hljs javascript"><code class="javascript"><span class="hljs-built_in">String</span> staticPathPattern = <span class="hljs-keyword">this</span>.mvcProperties.getStaticPathPattern();<span class="hljs-comment">//"/**"</span></br>php

<span class="hljs-keyword">if</span> (!registry.hasMappingForPattern(staticPathPattern)) {</br>
            customizeResourceHandlerRegistration(</br>
                    registry.addResourceHandler(staticPathPattern)</br>
                            .addResourceLocations(</br>
                                    <span class="hljs-keyword">this</span>.resourceProperties.getStaticLocations())</br>
                    .setCachePeriod(cachePeriod));</br>
        }</br>

</code></pre>css

<p>默認的靜態資源映射路徑在ResourceProperties類裏面,相關代碼以下:</p> <pre class="hljs php"><code class="php"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String[] CLASSPATH_RESOURCE_LOCATIONS = {</br> <span class="hljs-string">"classpath:/META-INF/resources/"</span>, <span class="hljs-string">"classpath:/resources/"</span>,</br> <span class="hljs-string">"classpath:/static/"</span>, <span class="hljs-string">"classpath:/public/"</span> };</br></br>html

<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String[] RESOURCE_LOCATIONS;</br></br>

<span class="hljs-keyword">static</span> {</br>
    RESOURCE_LOCATIONS = <span class="hljs-keyword">new</span> String[CLASSPATH_RESOURCE_LOCATIONS.length</br>
            + SERVLET_RESOURCE_LOCATIONS.length];</br>
    System.arraycopy(SERVLET_RESOURCE_LOCATIONS, <span class="hljs-number">0</span>, RESOURCE_LOCATIONS, <span class="hljs-number">0</span>,</br>
            SERVLET_RESOURCE_LOCATIONS.length);</br>
    System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, <span class="hljs-number">0</span>, RESOURCE_LOCATIONS,</br>
            SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);</br>
}</br></br>

<span class="hljs-comment">/**</br>
 * Locations of static resources. Defaults to classpath:[/META-INF/resources/,</br>
 * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).</br>
 */</span></br>
<span class="hljs-keyword">private</span> String[] staticLocations = RESOURCE_LOCATIONS;</br>

</code></pre>前端

<p>即默認配置的 /** 映射到以下目錄:</p> <blockquote> <p>classpath:/META-INF/resources/<br> classpath:/resources/<br> classpath:/static/<br> classpath:/public/</p> </blockquote> <p>綜上所述,Spring Boot 默認配置爲:</p> <table> <thead> <tr> <th>頁面請求路徑模式</th> <th>靜態資源在工程的路徑</th> <th>優先級</th> </tr> </thead> <tbody> <tr> <td>/**</td> <td>classpath:/META-INF/resources/</td> <td>第1優先</td> </tr> <tr> <td>/**</td> <td>classpath:/resources/</td> <td>第2優先</td> </tr> <tr> <td>/**</td> <td>classpath:/META-INF/resources/</td> <td>第3優先</td> </tr> <tr> <td>/**</td> <td>classpath:/static/</td> <td>第4優先</td> </tr> <tr> <td>/webjars/**</td> <td>classpath:/META-INF/resources/webjars/</td> <td>-</td> </tr> </tbody> </table> <h2>自定義靜態資源映射</h2> <p>在SpringBoot集成Swagger的時候,咱們須要增長swagger-ui.html映射到classpath:/META-INF/resources/,咱們自定義配置類,繼承WebMvcConfigurerAdapter,而後重寫addResourceHandlers方法:</p> <pre class="hljs java"><code class="java"><span class="hljs-meta">@Configuration</span></br> <span class="hljs-comment">//@EnableWebMvc //這個註解會覆蓋掉SpringBoot的默認的靜態資源映射配置</span></br> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebMvcConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WebMvcConfigurerAdapter</span> </span>{</br> <span class="hljs-meta">@Override</span></br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">addResourceHandlers</span><span class="hljs-params">(ResourceHandlerRegistry registry)</span> </span>{</br></br>java

<span class="hljs-comment">//Swagger ui Mapping</span></br>
    registry.addResourceHandler(<span class="hljs-string">"swagger-ui.html"</span>)</br>
            .addResourceLocations(<span class="hljs-string">"classpath:/META-INF/resources/"</span>)</br></br>

}</br></br>

}</br> </code></pre>jquery

<p>若是想要本身徹底控制WebMVC(徹底覆蓋SpringBoot默認配置),就須要在@Configuration註解的配置類上增長@EnableWebMvc,當增長@EnableWebMvc註解之後,WebMvcAutoConfiguration中配置就不會生效,會自動覆蓋了官方給出的/static, /public, META-INF/resources, /resources等存放靜態資源的目錄。而將靜態資源定位於src/main/webapp。</p> <p>在spring-boot-features.adoc中指出,若是你的應用要打成jar形式來運行的話,不要把靜態資源放到src/main/webapp目錄,雖然這是標準目錄,可是僅在打war包的時候起做用。由於大多數的構建工具在打jar包的時候,會默認忽略此目錄:</p> <blockquote> <p>TIP: Do not use the <code>src/main/webapp</code> directory if your application will be packaged as a<br> jar. Although this directory is a common standard, it will <em>only</em> work with war packaging<br> and it will be silently ignored by most build tools if you generate a jar.</p> </blockquote> <p>當須要從新定義好資源所在目錄時,則須要主動添加上述的那個配置類,來Override addResourceHandlers方法。你須要本身來配置須要的每一項。</p> <p>這種方式會在默認的基礎上增長</p> <blockquote> <p>swagger-ui.html映射到classpath:/META-INF/resources/</p> </blockquote> <p>不會影響默認的方式,能夠同時使用。</p> <h2>前端資源的引用方法</h2> <p>在index.ftl中該如何引用上面的靜態資源呢?<br> 推薦默認的寫法以下:</p> <pre class="hljs javascript"><code class="javascript">&lt;link rel=<span class="hljs-string">"stylesheet"</span> type=<span class="hljs-string">"text/css"</span> href=<span class="hljs-string">"/css/index.css"</span>&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/js/index.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span> </code></pre> <p>注意:默認配置的/**映射到/static(或/public ,/resources,/META-INF/resources)</p> <p>當請求/css/index.css的時候,Spring MVC 會在/static/目錄下面找到。</p> <p>若是配置爲/static/js/index.js</p> <pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/static/js/index.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> </code></pre> <p>那麼上面配置的幾個目錄下面都沒有/static目錄,所以會找不到資源文件。</p> <p>這個時候,就須要另外添加自定義的映射了:</p> <pre class="hljs bash"><code class="bash">registry.addResourceHandler(<span class="hljs-string">"/static/**"</span>) .addResourceLocations(<span class="hljs-string">"classpath:/static/"</span>) </code></pre> <blockquote> <p>因此,當咱們使用SpringBoot默認靜態資源配置的時候,寫靜態資源位置不要帶上映射的目錄名(如/static/,/public/ ,/resources/,/META-INF/resources/)。</p> </blockquote> <h2>使用WebJars</h2> <p>Spring Boot 在支持 Spring MVC的靜態資源處理的特性的同時, 容許使用jar包版本的靜態資源和使用版本無關的URL的靜態資源的引用。它就是Webjars[1]。</p> <p>例如,使用jQuery,添加依賴:</p> <pre class="hljs bash"><code class="bash">compile group: <span class="hljs-string">'org.webjars.bower'</span>, name: <span class="hljs-string">'jquery'</span>, version: <span class="hljs-string">'3.2.1'</span> </code></pre> <p>而後,在前端html代碼中,就能夠直接以下使用:</p> <pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/webjars/jquery/3.2.1/jquery.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> </code></pre> <p>你可能注意到href中的1.11.3版本號了,若是僅僅這麼使用,那麼當咱們切換版本號的時候還要手動修改href,比較麻煩。咱們徹底能夠約定一套目錄規則,把後端webjars的依賴版本,直接傳遞到後端。而負責完成維護管理這套目錄規則的人就是webjars-locator。webjars-locator經過在classpath中尋找須要加載的靜態資源,而後引入前端頁面。查找路徑的邏輯的方法是WebJarAssetLocator類裏的getFullPath方法。</p> <p>咱們要想使用webjars-locator,先要添加依賴:</p> <pre class="hljs java"><code class="java"> <span class="hljs-comment">//Group: org.webjars.bower</span> <span class="hljs-comment">// https://mvnrepository.com/artifact/org.webjars/webjars-locator</span> compile group: <span class="hljs-string">'org.webjars'</span>, name: <span class="hljs-string">'webjars-locator'</span>, version: <span class="hljs-string">'0.32'</span> </code></pre> <p>而後,增長一個WebJarController:</p> <pre class="hljs javascript"><code class="javascript">package com.easy.springboot.h5perf.controller</br></br>git

<span class="hljs-keyword">import</span> org.springframework.core.io.ClassPathResource</br> <span class="hljs-keyword">import</span> org.springframework.http.HttpStatus</br> <span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity</br> <span class="hljs-keyword">import</span> org.springframework.stereotype.Controller</br> <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.PathVariable</br> <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping</br> <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.ResponseBody</br> <span class="hljs-keyword">import</span> org.springframework.web.servlet.HandlerMapping</br> <span class="hljs-keyword">import</span> org.webjars.WebJarAssetLocator</br></br>github

<span class="hljs-keyword">import</span> javax.servlet.http.HttpServletRequest</br></br>web

<span class="hljs-comment">/**</br>

  • Created by jack on 2017/4/22.</br> */</span></br> @Controller</br> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebJarsController</span> </span>{</br> WebJarAssetLocator assetLocator = <span class="hljs-keyword">new</span> WebJarAssetLocator();</br></br>

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

</code></pre>

<p>而後,前端代碼使用方式以下:</p> <pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/datatables/jquery.dataTables.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span></br> <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/tether/tether.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span></br> <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"${request.contextPath}/css/index.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span></br> <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"${request.contextPath}/css/report.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span></br></br>

<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/jquery/jquery.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></br> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/js/index.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></br></br>

<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/tether/tether.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></br> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/bootstrap/bootstrap.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></br> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"${request.contextPath}/webjarslocator/datatables/jquery.dataTables.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></br></br>

</code></pre>

<p>這裏是freemarker的模板view代碼示例。其中,request對象是內置對象。在application.yml配置以下:</p> <pre class="hljs bash"><code class="bash">spring:</br> <span class="hljs-comment"># 在freemarker獲取request對象</span></br> freemarker:</br> request-context-attribute: request</br> </code></pre> <p>注意:這裏不須要在寫版本號了,可是注意寫url的時候,只是在原來url基礎上去掉了版本號,其餘的都不能少!</p> <h2>靜態資源動態版本</h2> <p>當咱們資源內容發生變化時,因爲瀏覽器緩存,用戶本地的靜態資源仍是舊的資源,爲了防止這種狀況致使的問題,咱們在請求url的時候加個版本號。</p> <p>版本號如:</p> <pre class="hljs xml"><code class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/js/index.js?v=1.0.1"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> </code></pre> <p>這個版本號1.0.1,能夠由後端代碼傳到前端頁面${version}。</p> <h1>小結</h1> <p>本章節主要探討了Spring Boot 靜態資源處理的內容。當咱們在開發中,遵循SpringBoot的默認配置,能夠大大減小了咱們靜態資源處理的工做。</p> <p>本章節完整的工程代碼:</p> <p><a href="https://link.jianshu.com?t=https://github.com/EasySpringBoot/h5perf" target="_blank" rel="nofollow">https://github.com/EasySpringBoot/h5perf</a></p> <p>參考資料:</p> <p>1.WebJars:<a href="https://link.jianshu.com?t=http://www.webjars.org/" target="_blank" rel="nofollow">http://www.webjars.org/</a></p>

</div>
    </div>
</div>
相關文章
相關標籤/搜索