Configure a cache busting resource chain

This commit configures a resource chain with
`resources.chain.strategy.content.enabled`, enabling cache busting for
static resources based on the hash of their content.

A template helper (here a Groovy Closure) is added to the model given to
the view for appending hashes to static links such as:

    "/css/spring.css" -> "/css/spring-d35c4193cd32e7e44cda5737205c0c0e.css"

Fixes #321
This commit is contained in:
Brian Clozel
2016-12-01 12:07:47 +01:00
parent 9967ebc3ad
commit 4de86e1401
7 changed files with 37 additions and 13 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.AsyncConfigurerSupport
import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
import org.springframework.web.servlet.resource.ResourceUrlProvider
/**
* Initializr service application. Enables legacy STS support for older
@@ -47,8 +48,9 @@ class InitializrService {
@Bean
@SuppressWarnings("deprecation")
LegacyStsController legacyStsController(InitializrMetadataProvider metadataProvider,
ResourceUrlProvider resourceUrlProvider,
GroovyTemplate groovyTemplate) {
new LegacyStsController(metadataProvider, groovyTemplate)
new LegacyStsController(metadataProvider, resourceUrlProvider, groovyTemplate)
}
@Configuration

View File

@@ -13,6 +13,11 @@ spring:
jackson:
serialization:
write-dates-as-timestamps: false
resources:
chain:
strategy:
content:
enabled: true
initializr:
env:

View File

@@ -16,6 +16,8 @@
package io.spring.initializr.web.autoconfigure
import org.springframework.web.servlet.resource.ResourceUrlProvider
import java.util.concurrent.TimeUnit
import com.github.benmanes.caffeine.cache.Caffeine
@@ -77,9 +79,11 @@ class InitializrAutoConfiguration {
@ConditionalOnMissingBean
MainController initializrMainController(InitializrMetadataProvider metadataProvider,
GroovyTemplate groovyTemplate,
ResourceUrlProvider resourceUrlProvider,
ProjectGenerator projectGenerator,
DependencyMetadataProvider dependencyMetadataProvider) {
new MainController(metadataProvider, groovyTemplate, projectGenerator, dependencyMetadataProvider)
new MainController(metadataProvider, groovyTemplate, resourceUrlProvider
, projectGenerator, dependencyMetadataProvider)
}
@Bean

View File

@@ -16,6 +16,8 @@
package io.spring.initializr.web.project
import org.springframework.web.servlet.resource.ResourceUrlProvider
import javax.servlet.http.HttpServletResponse
import io.spring.initializr.generator.InvalidProjectRequestException
@@ -36,12 +38,15 @@ abstract class AbstractInitializrController {
protected final InitializrMetadataProvider metadataProvider
private final GroovyTemplate groovyTemplate
private final Closure<String> linkTo
private Boolean forceSsl
protected AbstractInitializrController(InitializrMetadataProvider metadataProvider,
ResourceUrlProvider resourceUrlProvider,
GroovyTemplate groovyTemplate) {
this.metadataProvider = metadataProvider
this.groovyTemplate = groovyTemplate
this.linkTo = { link -> resourceUrlProvider.getForLookupPath(link)?:link }
}
boolean isForceSsl() {
@@ -79,6 +84,9 @@ abstract class AbstractInitializrController {
// Google analytics support
model['trackingCode'] = metadata.configuration.env.googleAnalyticsTrackingCode
// Linking to static resources
model['linkTo'] = this.linkTo
groovyTemplate.process templatePath, model
}

View File

@@ -22,6 +22,7 @@ import io.spring.initializr.util.GroovyTemplate
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.servlet.resource.ResourceUrlProvider
/**
* A controller used to serve the legacy home page used by STS.
@@ -33,8 +34,9 @@ import org.springframework.web.bind.annotation.ResponseBody
@Deprecated
class LegacyStsController extends AbstractInitializrController {
LegacyStsController(InitializrMetadataProvider metadataProvider, GroovyTemplate groovyTemplate) {
super(metadataProvider, groovyTemplate)
LegacyStsController(InitializrMetadataProvider metadataProvider, ResourceUrlProvider resourceUrlProvider,
GroovyTemplate groovyTemplate) {
super(metadataProvider, resourceUrlProvider, groovyTemplate)
}
@RequestMapping(value = '/sts', produces = 'text/html')

View File

@@ -16,6 +16,8 @@
package io.spring.initializr.web.project
import org.springframework.web.servlet.resource.ResourceUrlProvider
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit
@@ -74,8 +76,9 @@ class MainController extends AbstractInitializrController {
private final CommandLineHelpGenerator commandLineHelpGenerator
MainController(InitializrMetadataProvider metadataProvider, GroovyTemplate groovyTemplate,
ProjectGenerator projectGenerator, DependencyMetadataProvider dependencyMetadataProvider) {
super(metadataProvider, groovyTemplate)
ResourceUrlProvider resourceUrlProvider, ProjectGenerator projectGenerator,
DependencyMetadataProvider dependencyMetadataProvider) {
super(metadataProvider, resourceUrlProvider, groovyTemplate)
this.projectGenerator = projectGenerator
this.dependencyMetadataProvider = dependencyMetadataProvider
this.commandLineHelpGenerator = new CommandLineHelpGenerator(groovyTemplate)

View File

@@ -3,9 +3,9 @@
<head>
<title>Spring Initializr</title>
<link href="//fonts.googleapis.com/css?family=Varela+Round|Montserrat:400,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/bootstrap-theme.min.css"/>
<link rel="stylesheet" href="/css/spring.css"/>
<link rel="stylesheet" href="${linkTo.call('/css/bootstrap.min.css')}"/>
<link rel="stylesheet" href="${linkTo.call('/css/bootstrap-theme.min.css')}"/>
<link rel="stylesheet" href="${linkTo.call('/css/spring.css')}"/>
<link rel="shortcut icon" type="image/x-icon" href="/img/favicon.png"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
@@ -170,10 +170,10 @@
and <a href="https://run.pivotal.io">Pivotal Web Services</a></p>
</div>
</footer>
<script src="/js/jquery-3.1.1.min.js"></script>
<script src="/js/typeahead.bundle.min.js"></script>
<script src="/js/mousetrap.min.js"></script>
<script src="/js/start.js"></script>
<script src="${linkTo.call('/js/jquery-3.1.1.min.js')}"></script>
<script src="${linkTo.call('/js/typeahead.bundle.min.js')}"></script>
<script src="${linkTo.call('/js/mousetrap.min.js')}"></script>
<script src="${linkTo.call('/js/start.js')}"></script>
<% if (trackingCode) { %>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){