Cacheable Static Assets mit Spring MVC
1. Überblick
Dieser Artikel befasst sich mit der Zwischenspeicherung statischer Elemente (z. B. JavaScript- und CSS-Dateien), wenn diese mit Spring MVC bereitgestellt werden.
Wir werden auch auf das Konzept des "perfekten Caching" eingehen und im Wesentlichen sicherstellen, dass beim Aktualisieren einer Datei die alte Version nicht fälschlicherweise aus dem Cache bereitgestellt wird.
2. Zwischenspeichern statischer Assets
Damit statische Assets zwischengespeichert werden können, müssen Sie den entsprechenden Ressourcenhandler konfigurieren.
Hier ist ein einfaches Beispiel dafür: Setzen Sie den HeaderCache-Control in der Antwort aufmax-age=31536000, wodurch der Browser die zwischengespeicherte Version der Datei ein Jahr lang verwendet:
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
Der Grund dafür, dass wir einen so langen Zeitraum für die Gültigkeit des Caches haben, ist, dass der Client die zwischengespeicherte Version der Datei verwenden soll, bis die Datei aktualisiert wird. 365{'t0': 'RFC for the Cache-Control header'}ind das Maximum, das wir gemäßRFC for the Cache-Control header verwenden können.
And so, when a client requests foo.js for the first time erhält er die gesamte Datei über das Netzwerk (in diesem Fall 37 Byte) mit dem Statuscode200 OK.. Die Antwort hat den folgenden Header, um das Caching-Verhalten zu steuern:
Cache-Control: max-age=31536000
Dies führt dazu, dass der Browser die Datei mit einer Ablaufdauer von einem Jahr zwischenspeichert, was auf die folgende Antwort zurückzuführen ist:
When the client requests the same file for the second time, der Browser stellt keine weitere Anfrage an den Server. Stattdessen wird die Datei direkt aus dem Cache bereitgestellt und das Netzwerk-Roundtrip vermieden, sodass die Seite viel schneller geladen wird:
Benutzer von Chrome-Browsern müssen beim Testen vorsichtig sein, da Chrome den Cache nicht verwendet, wenn Sie die Seite durch Drücken der Schaltfläche Aktualisieren auf dem Bildschirm oder durch Drücken der Taste F5 aktualisieren. Sie müssen die Eingabetaste in der Adressleiste drücken, um das Caching-Verhalten zu beobachten. Weitere Infos zu diesemhere.
3. Versionierung statischer Assets
Durch die Verwendung eines Caches für die Bereitstellung der statischen Assets wird die Seite zwar sehr schnell geladen, weist jedoch eine wichtige Einschränkung auf. Wenn Sie die Datei aktualisieren, erhält der Client nicht die neueste Version der Datei, da er nicht mit dem Server überprüft, ob die Datei aktuell ist und nur die Datei aus dem Browser-Cache bereitstellt.
Folgendes müssen wir tun, damit der Browser die Datei nur dann vom Server erhält, wenn die Datei aktualisiert wird:
-
Stellen Sie die Datei unter einer URL bereit, die eine Version enthält. Zum Beispiel solltenfoo.js unter/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js serviert werden
-
Aktualisieren Sie die Links zur Datei mit der neuen URL
-
Aktualisieren Sie die Version eines Teils der URL, wenn die Datei aktualisiert wird. Wenn beispielsweisefoo.js aktualisiert wird, sollte es jetzt unter/js/foo-a3d8d7780349a12d739799e9aa7d2623.js. bereitgestellt werden
Der Client fordert die Datei beim Aktualisieren vom Server an, da die Seite einen Link zu einer anderen URL enthält, sodass der Browser den Cache nicht verwendet. Wenn eine Datei nicht aktualisiert wird, ändert sich ihre Version (daher ihre URL) nicht und der Client verwendet weiterhin den Cache für diese Datei.
Normalerweise müssten wir dies alles manuell tun, aber Spring unterstützt dies sofort, einschließlich der Berechnung des Hashs für jede Datei und des Anhängens dieser an die URLs. Mal sehen, wie wir unsere Spring-Anwendung so konfigurieren können, dass sie all dies für uns erledigt.
3.1. Unter einer URL mit einer Version dienen
Wir müssen einem Pfad einVersionResourceResolver hinzufügen, um die Dateien darunter mit einer aktualisierten Versionszeichenfolge in der URL bereitzustellen:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
Hier verwenden wir eine Inhaltsversionsstrategie. Jede Datei im Ordner/jswird unter einer URL bereitgestellt, deren Inhalt eine Version berechnet. Dies wird als Fingerabdruck bezeichnet. Beispielsweise wirdfoo.js jetzt unter der URL/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js. bereitgestellt
Bei dieser Konfiguration, wenn ein Client eine Anforderung fürhttp://localhost:8080/js/_46944c7e3a9bd20cc30fdc085cae46f2.js:_ stellt
curl -i http://localhost:8080/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js
Der Server antwortet mit einem Cache-Control-Header, um den Client-Browser anzuweisen, die Datei für ein Jahr zwischenzuspeichern:
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. Aktualisieren Sie Links mit der neuen URL
Bevor wir die Version in die URL einfügen, können wir ein einfachesscript-Tag verwenden, umfoo.js zu importieren:
Nachdem wir dieselbe Datei unter einer URL mit einer Version bereitgestellt haben, müssen wir sie auf der Seite wiedergeben:
Es wird mühsam, mit all diesen langen Wegen umzugehen. Es gibt eine bessere Lösung, die Spring für dieses Problem bietet. Wir könnenResourceUrlEncodingFilter undurl von JSTL verwenden, um die URLs der Links mit versionierten umzuschreiben.
ResourceURLEncodingFilter können wie gewohnt unterweb.xml registriert werden:
resourceUrlEncodingFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
resourceUrlEncodingFilter
/*
Die JSTL-Core-Tag-Bibliothek muss auf unserer JSP-Seite importiert werden, bevor wir dasurl-Tag verwenden können:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Dann können wir dasurl-Tag verwenden, umfoo.js wie folgt zu importieren:
">
Wenn diese JSP-Seite gerendert wird, wird die URL für die Datei korrekt umgeschrieben, um die darin enthaltene Version zu enthalten:
3.3. Version aktualisieren Teil der URL
Immer wenn eine Datei aktualisiert wird, wird ihre Version erneut berechnet und die Datei unter einer URL bereitgestellt, die die neue Version enthält. Wir müssen dafür keine zusätzliche Arbeit leisten,VersionResourceResolver erledigt dies für uns.
4. Korrigieren Sie CSS-Links
CSS-Dateien können mithilfe der Anweisungen von@importandere CSS-Dateien importieren. Beispiel: Die DateimyCss.cssimportiert die Dateianother.css:
@import "another.css";
Dies würde normalerweise Probleme mit versionierten statischen Assets verursachen, da der Browser eine Anforderung für die Dateianother.css stellt, die Datei jedoch unter einem versionierten Pfad wieanother-9556ab93ae179f87b178cfad96a6ab72.css. bereitgestellt wird
Um dieses Problem zu beheben und eine Anfrage an den richtigen Pfad zu stellen, müssen wirCssLinkResourceTransformer in die Konfiguration des Ressourcenhandlers einführen:
@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());
}
Dies ändert den Inhalt vonmyCss.css und tauscht die Importanweisung wie folgt aus:
@import "another-9556ab93ae179f87b178cfad96a6ab72.css";
5. Fazit
Das HTTP-Caching zu nutzen, ist eine enorme Verbesserung der Website-Leistung. Es kann jedoch umständlich sein, zu vermeiden, dass veraltete Ressourcen bei der Verwendung von Caching bereitgestellt werden.
In diesem Artikel haben wir eine gute Strategie implementiert, um HTTP-Caching zu verwenden, während statische Assets mit Spring MVC bereitgestellt werden und der Cache beim Aktualisieren der Dateien gelöscht wird.
Den Quellcode für diesen Artikel finden Sie unterGitHub.