Spring的静态资产缓存

Ala*_*ruz 6 javascript java spring spring-mvc spring-boot

我正在开发一个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();
}
Run Code Online (Sandbox Code Playgroud)

除了一个简单的细节,一切都按预期进行。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即使有新版本,也要使用旧的import加载它。

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

Pas*_*kam 5

解决这个缓存版本的方法是使用应用程序版本。如果该项目是基于Maven构建的,那么我看到您正在使用classpath资源进行静态文件解析。每当js文件发生新更改时,您都将拥有新的构建,并且如果可以在每个构建上更改版本,则这是我的解决方法。

pom.xml

<version>0.1.0</version>
<build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
</resources>
Run Code Online (Sandbox Code Playgroud)

application.yml

build:
  version: @project.version@
Run Code Online (Sandbox Code Playgroud)

这会将版本从pom.xml推送到IDE上的开发人员和内置jar的application.yml

控制者

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

@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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

处理现有的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());
                }
            });
    }

}
Run Code Online (Sandbox Code Playgroud)

pop.js

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

由于版本已添加,ModelAttribute因此它将在所有请求映射中可用。对于每个版本,此设置都会更改,并且使用此缓存版本变量可以提取文件。