Actifs statiques pouvant être mis en cache avec Spring MVC
1. Vue d'ensemble
Cet article traite de la mise en cache des ressources statiques (telles que les fichiers Javascript et CSS) lors de leur utilisation avec Spring MVC.
Nous aborderons également le concept de «mise en cache parfaite», en veillant essentiellement à ce que, lorsqu'un fichier est mis à jour, l'ancienne version ne soit pas diffusée de manière incorrecte à partir du cache.
2. Mise en cache des actifs statiques
Afin de rendre les actifs statiques pouvant être mis en cache, nous devons configurer le gestionnaire de ressources correspondant.
Voici un exemple simple de la façon de procéder: définir l'en-têteCache-Control de la réponse surmax-age=31536000, ce qui oblige le navigateur à utiliser la version mise en cache du fichier pendant un an:
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
La raison pour laquelle nous avons une période de validité du cache si longue est que nous voulons que le client utilise la version mise en cache du fichier jusqu'à ce que le fichier soit mis à jour, et 365 jours est le maximum que nous pouvons utiliser selon lesRFC for the Cache-Control header.
And so, when a client requests foo.js for the first time, il recevra l'intégralité du fichier sur le réseau (37 octets dans ce cas) avec un code d'état de200 OK. La réponse aura l'en-tête suivant pour contrôler le comportement de la mise en cache:
Cache-Control: max-age=31536000
Le navigateur mettra alors le fichier en cache avec une durée d'expiration d'un an, à la suite de la réponse suivante:
When the client requests the same file for the second time, le navigateur ne fera pas une autre requête au serveur. Au lieu de cela, il servira directement le fichier à partir de son cache et évitera les allers-retours sur le réseau afin que la page se charge beaucoup plus rapidement:
Les utilisateurs de navigateur Chrome doivent faire preuve de prudence lors des tests, car Chrome n'utilisera pas le cache si vous actualisez la page en appuyant sur le bouton d'actualisation à l'écran ou en appuyant sur la touche F5. Vous devez appuyer sur Entrée dans la barre d'adresse pour observer le comportement de la mise en cache. Plus d'informations sur ceshere.
3. Gestion des versions des actifs statiques
L'utilisation d'un cache pour servir les actifs statiques accélère le chargement de la page, mais il y a une mise en garde importante. Lorsque vous mettez à jour le fichier, le client ne recevra pas la version la plus récente du fichier car il ne vérifie pas auprès du serveur si le fichier est à jour et ne sert que le fichier à partir du cache du navigateur.
Voici ce que nous devons faire pour que le navigateur récupère le fichier du serveur uniquement lorsque le fichier est mis à jour:
-
Servir le fichier sous une URL comportant une version. Par exemple,foo.js doit être servi sous/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js
-
Mettre à jour les liens vers le fichier avec la nouvelle URL
-
Mettre à jour la partie de la version de l'URL chaque fois que le fichier est mis à jour. Par exemple, lorsquefoo.js est mis à jour, il doit maintenant être servi sous/js/foo-a3d8d7780349a12d739799e9aa7d2623.js.
Le client demandera le fichier au serveur lors de sa mise à jour car la page aura un lien vers une URL différente, de sorte que le navigateur n'utilisera pas son cache. Si un fichier n'est pas mis à jour, sa version (donc son URL) ne changera pas et le client continuera à utiliser le cache pour ce fichier.
Normalement, nous aurions besoin de toutes ces tâches manuellement, mais Spring les prend en charge immédiatement, notamment en calculant le hachage de chaque fichier et en les ajoutant aux URL. Voyons comment nous pouvons configurer notre application Spring pour faire tout cela pour nous.
3.1. Servir sous une URL avec une version
Nous devons ajouter unVersionResourceResolver à un chemin afin de servir les fichiers sous celui-ci avec une chaîne de version mise à jour dans son URL:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
Ici, nous utilisons une stratégie de version de contenu. Chaque fichier du dossier/js sera servi sous une URL dont la version est calculée à partir de son contenu. Ceci s'appelle l'empreinte digitale. Par exemple,foo.js sera désormais diffusé sous l'URL/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js.
Avec cette configuration, lorsqu'un client fait une demande pourhttp://localhost:8080/js/_46944c7e3a9bd20cc30fdc085cae46f2.js:_
curl -i http://localhost:8080/js/foo-46944c7e3a9bd20cc30fdc085cae46f2.js
Le serveur répondra par un en-tête Cache-Control pour indiquer au navigateur client de mettre le fichier en cache pendant un an:
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. Mettre à jour les liens avec la nouvelle URL
Avant d'insérer la version dans l'URL, nous pourrions utiliser une simple balisescript pour importerfoo.js:
Maintenant que nous servons le même fichier sous une URL avec une version, nous devons le refléter sur la page:
Il devient fastidieux de gérer tous ces longs chemins. Il existe une meilleure solution que Spring propose pour ce problème. Nous pouvons utiliserResourceUrlEncodingFilter et la baliseurl de JSTL pour réécrire les URL des liens avec des versions.
ResourceURLEncodingFilter peut être enregistré sousweb.xml comme d'habitude:
resourceUrlEncodingFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
resourceUrlEncodingFilter
/*
La bibliothèque de balises de base JSTL doit être importée sur notre page JSP avant de pouvoir utiliser la baliseurl:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Ensuite, nous pouvons utiliser la baliseurl pour importerfoo.js comme suit:
">
Lorsque cette page JSP est rendue, l'URL du fichier est réécrite correctement pour contenir la version:
3.3. Mettre à jour une partie de la version de l'URL
Chaque fois qu'un fichier est mis à jour, sa version est à nouveau calculée et le fichier est servi sous une URL contenant la nouvelle version. Nous n'avons pas à faire de travail supplémentaire pour cela,VersionResourceResolver s'en charge pour nous.
4. Corriger les liens CSS
Les fichiers CSS peuvent importer d'autres fichiers CSS en utilisant les directives@import. Par exemple, le fichiermyCss.css importe le fichieranother.css:
@import "another.css";
Cela poserait normalement des problèmes avec les ressources statiques versionnées, car le navigateur fera une demande pour le fichieranother.css, mais le fichier est servi sous un chemin versionné tel queanother-9556ab93ae179f87b178cfad96a6ab72.css.
Pour résoudre ce problème et faire une requête vers le chemin correct, nous devons introduireCssLinkResourceTransformer dans la configuration du gestionnaire de ressources:
@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());
}
Cela modifie le contenu demyCss.css et permute l'instruction d'importation avec ce qui suit:
@import "another-9556ab93ae179f87b178cfad96a6ab72.css";
5. Conclusion
Tirer parti de la mise en cache HTTP augmente considérablement les performances des sites Web, mais il peut être fastidieux d’éviter de servir des ressources obsolètes lors de la mise en cache.
Dans cet article, nous avons mis en œuvre une bonne stratégie pour utiliser la mise en cache HTTP lors de la gestion d'actifs statiques avec Spring MVC et de la suppression du cache lorsque les fichiers sont mis à jour.
Vous pouvez trouver le code source de cet article surGitHub.