为 SpringBoot Jar 包配置静态文件缓存

熟悉 java 的读者应该都知道,我们可以利用 springboot 快速生成一个包含 tomcat 的 jar 包,部署到任意安装了 java 运行时的环境下运行,最近发现了一个问题,这个开箱即用的 jar,并没有启用对静态文件的缓存

1 问题

最近观察资源监视器时发现了一个问题,即使请求很少的情况下,系统依然消耗了很大的流量。打开浏览器的开发者模式,发现所有静态资源资源都没有使用缓存,也就是说,每次打开网页,所有资源都是重新加载的,这就是问题所在了

2 重现

2.1 新建 SpringBoot 项目

我们可以在 Spring Initializr 创建一个测试项目,使用 maven 构建,只选择 web、 thymeleaf 和 devtools 作为依赖,点 Generate 生成项目包,如图所示

解压生成的项目,等待后续使用

2.2 复制 Bootstrap 到 static 目录

我们以 Bootstrap 4 为例来示范对静态资源的引用,目前最新的 Bootstrap 可以从这里下载,下载后解压到项目的 src/main/resources/static 目录下

2.3 新建测试 html 页面

在项目的 src/main/resources/templates 内创建一个测试文件 test.html

文件内容如下

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="bootstrap-4.3.1-dist/css/bootstrap.min.css">
</head>
<body>
  test.html body
</body>

</html>

你可以看到这个文件并没有实质性内容,只是引用了 Bootstrap 的 css 文件

2.4 新建测试 controller

新建 TestController.java,内容如下:

package one.tricks.java.staticcachedemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class TestController {
    @RequestMapping(path = "test")
    public ModelAndView test(ModelAndView mav) {
	mav.setViewName("test");
	return mav;
    }
}

这个 controller 也很简单,响应 test 路由,返回 test.html 给客户端

2.5 测试

接下来我们可以试运行我们的项目了,我们同时也打开浏览器的开发者模式,访问测试地址 http://localhost:8080/test 来测试几次,检查 css 文件的加载方式

我们可以看到 css 文件并没有使用缓存

3 原因

没有使用缓存的原因是我们启用了 devtools 依赖,读者可以取消 devtools 重试一次,再来检查加载方式

可以看到返回码变为 304,使用了缓存,不过 devtools 是一个蛮常用的依赖,有没有其他方法可以不改变依赖同时也可以使用缓存呢?

4 解决

我们可以在 application.properties 加入以下配置

spring.resources.cache.cachecontrol.cache-private=true
spring.resources.cache.cachecontrol.must-revalidate=true
spring.resources.cache.cachecontrol.max-age=31536000
spring.resources.static-locations=classpath:/static/

然后重新进行测试

我们可以看到,虽然返回码依然是 200,但是浏览器已经使用了缓存,这里就不再对相关配置的意义进行展开,有兴趣的读者可以从这里查阅

5 总结

SpringBoot 项目在启用了一些依赖以后,会丢失对静态文件缓存的 header,此时我们可以通过增加配置的方法恢复,当然也有其他方法,不过我觉得增添配置的方法比较简单