1概要

この記事では、Spring MVCで静的アセット(JavascriptやCSSファイルなど)をキャッシュする際のキャッシングについて説明します。

また、ファイルが更新されたときに、古いバージョンが誤ってキャッシュから提供されることがないように、「完全キャッシュ」の概念にも触れます。


2静的資産のキャッシング

静的資産をキャッシュ可能にするためには、対応するリソースハンドラを設定する必要があります。

その方法の簡単な例を示します。レスポンスの

Cache-Control

ヘッダーを

max-age = 31536000

に設定すると、ブラウザはキャッシュされたバージョンのファイルを1年間使用します。

@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/js/** ** ")
                .addResourceLocations("/js/")
                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
    }
}

キャッシュの有効期間が非常に長いのは、ファイルが更新されるまでクライアントにキャッシュされたバージョンのファイルを使用させ、https://wwwに従って使用できる最大日数は365日だからです。

Cache-Control

ヘッダーの場合はietf.org/rfc/rfc2616.txt[RFC]。

  • したがって、クライアントが初めて

    foo.js

    を要求すると、** 200 OKのステータスコードでネットワーク全体(この場合は37バイト)でファイル全体が受信されます。応答には次のヘッダーが含まれます。キャッシュ動作を制御します。

Cache-Control: max-age=31536000

これにより、ブラウザは、有効期限が1年のファイルを次のようにキャッシュします。

リンク:/uploads/cache-300×215.png%20300w[]

  • クライアントが同じファイルを2回要求しても、ブラウザはサーバーに別の要求をしません。代わりに、キャッシュからファイルを直接処理し、ネットワークのラウンドトリップを回避するので、ページの読み込みがはるかに速くなります。

リンク:/uploads/cache-highlighted-300×210.png%20300w[]

Chromeブラウザのユーザーは、画面の更新ボタンを押すかF5キーを押してページを更新すると、Chromeがキャッシュを使用しないため、テスト中に注意する必要があります。キャッシュ動作を確認するには、アドレスバーのEnterキーを押す必要があります。そのhttp://stackoverflow.com/questions/3401049/chrome-doesnt-cache-images-js-css/16510707#16510707[ここ]の詳細情報。


3静的資産のバージョニング

静的アセットを提供するためにキャッシュを使用すると、ページの読み込みが非常に速くなりますが、重要な注意点があります。ファイルを更新すると、クライアントはファイルが最新のものであるかどうかをサーバーに確認せず、ブラウザのキャッシュからファイルを提供するだけなので、ファイルの最新バージョンを取得しません。

ファイルが更新されたときにのみブラウザがサーバーからファイルを取得するようにするために必要なことは次のとおりです。

  • その中にバージョンがあるURLの下でファイルを出してください。例えば、


foo.js

は以下で提供されるべきです

/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js

** 新しいURLでファイルへのリンクを更新する

  • ファイルが更新されるたびにURLのバージョン部分を更新する。にとって

たとえば、

foo.js

が更新されると、

/js/foo-a3d8d7780349a12d739799e9aa7d2623.js.

の下に表示されるはずです。

ページには別のURLへのリンクがあるため、クライアントは更新されたときにサーバーにファイルを要求します。ブラウザはキャッシュを使用しません。ファイルが更新されない場合、そのバージョン(したがってそのURL)は変更されず、クライアントはそのファイルのキャッシュを使い続けます。

通常、これらすべてを手動で実行する必要がありますが、Springでは、ファイルごとにハッシュを計算してそれらをURLに追加するなど、すぐに使用できる状態でサポートしています。これをすべて実行するようにSpringアプリケーションを設定する方法を見てみましょう。


3.1. バージョン付きのURLで配信

そのURLの更新されたバージョン文字列でその下のファイルを提供するために、パスに

VersionResourceResolver

を追加する必要があります。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/js/** ** ")
            .addResourceLocations("/js/")
            .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
            .resourceChain(false)
            .addResolver(new VersionResourceResolver().addContentVersionStrategy("/** ** "));
}

ここでは、コンテンツバージョン戦略を使用します。

/js

フォルダー内の各ファイルは、そのコンテンツから計算されたバージョンを持つURLの下で提供されます。

これはフィンガープリントと呼ばれます。たとえば、

foo.js




/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js.__というURLで配信されます

この構成では、クライアントがhttp://localhost:8080/js/__46944c7e3a9bd20cc30fdc085cae46f2.js:を要求したとき

curl -i http://localhost:8080/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js

サーバーはCache-Controlヘッダーで応答して、クライアントブラウザに1年間ファイルをキャッシュするように指示します。

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 09 Aug 2016 06:43:26 GMT
Cache-Control: max-age=31536000


3.2. 新しいURLでリンクを更新する

URLにバージョンを挿入する前に、単純な

script

タグを使用して

foo.js

をインポートできます。

<script type="text/javascript" src="/js/foo.js">

同じファイルをバージョン付きのURLで提供したので、それをページに反映する必要があります。

<script type="text/javascript"
  src="<em>/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js</em>">

これらすべての長い道のりを扱うのは面倒です。 Springがこの問題に対して提供するよりよい解決策があります。リンクのURLをバージョン対応リンクに書き換えるには、

ResourceUrlEncodingFilter

とJSTLの

url

タグを使用できます。


ResourceURLEncodingFilter

は、通常どおり

web.xml

に登録できます。

<filter>
    <filter-name>resourceUrlEncodingFilter</filter-name>
    <filter-class>
        org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>resourceUrlEncodingFilter</filter-name>
    <url-pattern>/** </url-pattern>
</filter-mapping>


url

タグを使用する前に、JSTLコアタグライブラリをJSPページにインポートする必要があります。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

その後、次のように

url

タグを使用して

foo.js

をインポートできます。

<script type="text/javascript" src="<c:url value="/js/foo.js"/>">

このJSPページがレンダリングされると、ファイルのURLがバージョンを含むように正しく書き換えられます。

<script type="text/javascript" src="/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js">


3.3. 更新バージョンURLの一部

ファイルが更新されるたびに、そのバージョンは再度計算され、ファイルは新しいバージョンを含むURLの下で提供されます。このために追加の作業を行う必要はありません。VersionResourceResolver__がこれを処理します。


4 CSSリンクを修正する

CSSファイルは

@ import

ディレクティブを使用して他のCSSファイルをインポートできます。たとえば、

myCss.css

fileは

another.css

ファイルをインポートします。

@import "another.css";

ブラウザは

another.css

ファイルを要求しますが、ファイルは

another-9556ab93ae179f87b178cfad96a6ab72.css.

などのバージョン管理されたパスで提供されるため、これは通常、バージョン管理された静的アセットで問題を引き起こします。

この問題を解決して正しいパスへのリクエストを行うには、

CssLinkResourceTransformer

をリソースハンドラ設定に導入する必要があります。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/** ** ")
            .addResourceLocations("/resources/", "classpath:/other-resources/")
            .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
            .resourceChain(false)
            .addResolver(new VersionResourceResolver().addContentVersionStrategy("/** ** "))
            .addTransformer(new CssLinkResourceTransformer());
}

これにより

myCss.css

の内容が変更され、import文が次のように置き換えられます。

@import "another-9556ab93ae179f87b178cfad96a6ab72.css";


5結論

HTTPキャッシングを利用することはWebサイトのパフォーマンスを大きく向上させることですが、キャッシングを使用している間に古いリソースを提供することを避けるのは面倒かもしれません。

この記事では、Spring MVCで静的アセットを提供しながらファイルが更新されたときにキャッシュを無効にしながら、HTTPキャッシュを使用するための優れた戦略を実装しました。

この記事のソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-static-resources[GitHub]にあります。