简体   繁体   中英

Spring MVC and Thymeleaf Resource Versioning

资源层次结构我的项目

I am trying resource versioning with Spring Mvc 4.I use thymeleaf template engine.But doesnt work with the following code.When load the page I cant see new version Url when i view the page source.So what's the problem in my code? what am i miss?

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/theme*//**").addResourceLocations("/resources/static/theme/")
            .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
            .resourceChain(false)
            .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
            .addTransformer(new CssLinkResourceTransformer());
    registry.addResourceHandler("/static*//**").addResourceLocations("/resources/static/")
            .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
            .resourceChain(false)
            .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
            .addTransformer(new CssLinkResourceTransformer());
    registry.addResourceHandler("/static/js*//**").addResourceLocations("/resources/static/js/")
            .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
            .resourceChain(false)
            .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
            .addTransformer(new CssLinkResourceTransformer());
}

@Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
    return new ResourceUrlEncodingFilter();
}

I am using with expression in script tag. th:src="@{/resources/static/js/companyList.js}"

I managed it with no code, only config in application.properties:

# Enable HTML5 application cache manifest rewriting.
spring.resources.chain.html-application-cache=true

# Enable the Spring Resource Handling chain. Disabled by default unless at least one strategy has been enabled.
spring.resources.chain.enabled=true
# Enable the content Version Strategy.
spring.resources.chain.strategy.content.enabled=true
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.content.paths=/**

I didn't need to add any other code to get the hash version in the URLs for CSS and JS.

Here is the my solution.I debug source code of Spring.ServletContextResource class create a relativeRelative.Then check whether resource is exists.

Resource location : /resources/static/

Path : /static/css/login.css

pathToUse : /resources/static/static/css/login.css --> this resource url not exists so return null.

ServletContextResource class

@Override
public Resource createRelative(String relativePath) {
    String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
    return new ServletContextResource(this.servletContext, pathToUse);
}

Solution: Resource location : /resources/static/

Path : /css/login.css

pathToUse : /resources/static/css/login.css

Now I include this format.Remove /resources from path.

th:src="@{/css/login.css}"

           @Override
           public void addResourceHandlers(final ResourceHandlerRegistry registry) 
           {

                registry.addResourceHandler("/theme*//**").addResourceLocations("/resources/static/")
                        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                        .resourceChain(false)
                        .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                        .addTransformer(new CssLinkResourceTransformer());
                registry.addResourceHandler("/css*//**").addResourceLocations("/resources/static/")
                        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                        .resourceChain(false)
                        .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                        .addTransformer(new CssLinkResourceTransformer());
                registry.addResourceHandler("/js*//**").addResourceLocations("/resources/static/")
                        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                        .resourceChain(false)
                        .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                        .addTransformer(new CssLinkResourceTransformer());

         @Override
         public void configure(final WebSecurity web) throws Exception {
 web.ignoring().antMatchers("/theme/**").antMatchers("/js/**").antMatchers("/css/**");
            }

1. Create Thymeleaf LinkBuilder that uses Spring's ResourceUrlProvider to create versioned links:

@Configuration
public class TemplateEngineConfig {
    @Autowired
    public void configureTemplateEngine(SpringTemplateEngine engine,
                                        ResourceUrlProvider urlProvider) {
        engine.setLinkBuilder(new VersioningLinkBuilder(urlProvider));
    }
}

class VersioningLinkBuilder extends StandardLinkBuilder {
    private final ResourceUrlProvider urlProvider;

    VersioningLinkBuilder(ResourceUrlProvider urlProvider) {
        this.urlProvider = urlProvider;
    }

    @Override
    public String processLink(IExpressionContext context, String link) {
        String lookedUpLink = urlProvider.getForLookupPath(link);
        if (lookedUpLink != null) {
            return super.processLink(context, lookedUpLink);
        } else {
            return super.processLink(context, link);
        }
    }
}

2. Use thymeleaf tags th:href and th:src

<link th:href="@{/main.css}" rel="stylesheet" type="text/css"/>
<script th:src="@{/js/main.js}" type="text/javascript"></script>

It will be converted to:

<link href="/main-0c362e5c8643b75ddf64940262b219f7.css" rel="stylesheet" type="text/css"/>
<script src="/js/main-c13acb86fa1012e27bbb01a7c4a9bf7f.js" type="text/javascript"></script>

3. (Optional) It is also recommended to add browser cache headers. Add to your application.properties :

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.cache.cachecontrol.max-age=365d
spring.resources.cache.cachecontrol.no-cache=false
spring.resources.cache.cachecontrol.no-store=false
spring.resources.cache.cachecontrol.cache-public=true

Or if you use application.yml :

spring:
  resources:
    chain:
      strategy:
        content:
          enabled: true
          paths: /**
    cache:
      cachecontrol:
        max-age: 365d
        no-cache: false
        no-store: false
        cache-public: true

The usage of ResourceUrlEncodingFilter filters all urls in the page, which is not desirable and causes performance issues. My solution is the following:

registry.addResourceHandler("/javascript/*.js", "/css/*.css", "/img/*")
                    .addResourceLocations("classpath:/static/javascript/", "classpath:/static/css/", "classpath:/static/img/")
                    .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                    .resourceChain(true)
                    .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));

and inside the page using the below function to lookup the static resource

<script th:src="${@mvcResourceUrlProvider.getForLookupPath('/javascript/app.js')}"></script>

The following worked for me:

application.yml

...
resources:
  chain:
    strategy:
      content:
        enabled: true
        paths: /js/**,/css/**
...

index.html

...
<script th:src=@{/js/home.js}></script>
...

Result

This renders something like this:

...
<script src=/js/home-440273f30b71d3cf4184b48ce5e10b94.js></script>
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM