带有弹簧的静态资源缓存



我正在开发一个web应用程序,并打算利用缓存资源带来的性能提升,但它附带了一个重要的警告。每当我更新静态文件时,用户都不会立即看到这些更改,因此必须禁用浏览器的缓存才能获取最新版本。为了解决这个问题,我决定添加静态资产版本控制。它与以下代码一起按预期工作。

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
// Costume made transformer to handle JS imports
.addTransformer(new JsLinkResourceTransformer())
.addTransformer(new CssLinkResourceTransformer());
}
@Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}

除了一个简单的细节外,一切都按计划进行。JS导入仍在加载无版本的文件。所以像import * from './myscrypt.js'这样的东西不能正常工作。

为了避免新的警告,我不得不实现自己的资源转换器。实现完成了它的工作,现在我的导入将获取正确的版本,如import * from './myscript-149shdhgshs.js'。然后,我以为一切都解决了,但一个新问题出现了。这是一个场景,它将更容易理解。

  1. 我加载了一个包含script.js的页面
  2. 然后Spring为我提供正确版本的文件script-v1.js
  3. 之后,script-v1.jsmyscript.js导入函数
  4. 浏览器获取正确版本的脚本myscript-v1.js
  5. 他们两个在本地缓存
  6. 我更新了myscript.js,制作了新版本myscript-v2.js
  7. 我重新加载页面,但由于script-v1.js存储在缓存中,所以我使用旧的导入myscript-v1.js加载它,即使有新版本

我似乎就是无法让它工作。当然,我可以简单地停止使用js modules,而是一次加载所有脚本,但这不是我想要的解决方案。是否有使用Spring进行js module版本控制的解决方案?

我解决这个缓存版本的方法是使用应用程序版本。如果项目是在Maven上构建的,那么我看到您正在使用classpath资源进行静态文件解析。每当js文件有新的更改时,你就会有新的构建,如果你可以在每个构建中更改版本,下面是我的解决方法。

pom.xml

<version>0.1.0</version>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>

application.yml

build:
version: @project.version@

这将把pom.xml的版本推送到application.yml,包括IDE上的dev和构建的jar

控制器

我在这里使用胡子视图解析器。

@Controller
public class HelloController {
@Value("${build.version}")
private String version;
private String encodedVersion;
@PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}
@RequestMapping("/home")
public ModelAndView home() {
ModelAndView mv = new ModelAndView();
mv.setViewName("home.html");
return mv;
}
@ModelAttribute("version")
public String getVersion() {
return encodedVersion;
}
}

home.html

<html>
<head>
<script type="text/javascript" src="/pop.js?cache={{version}}"></script>
<script type="text/javascript">
window.version = "{{version}}" // in case you need this somewhere
</script>
</head>
<body>
<h1>Home1</h1>
version: {{version}}
</body>
</html>

操作现有的js文件

@Configuration
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class Config implements WebMvcConfigurer {
@Value("${build.version}")
private String version;
private String encodedVersion;
@PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/").setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)).resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new ResourceTransformer() {
@Override
public Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain) throws IOException {
// Be aware of side effects changing line break
String result = new BufferedReader(new InputStreamReader(resource.getInputStream())).lines().collect(Collectors.joining("n"));
result = result.replace("{{cacheVersion}}", encodedVersion);
return new TransformedResource(resource, result.getBytes());
}
});
}
}

pop.js

import mod1 from './mod1.js?cache={{cacheVersion}}';
function dis() {
console.log("hello")  
}

由于该版本被添加为ModelAttribute,它将在所有请求映射中可用。对于每个版本,这都将被更改,并且提取文件的方式可以使用此缓存版本变量。

最新更新