Cache templates

This commit improves massively class loading performance by caching all
templates used by the generator. GroovyTemplate is now a bean rather than
a private utility.

Templates are cached by default and the cache is disabled automatically
if Devtools is in use (via a check of spring.groovy.template.cache).

Closes gh-288
This commit is contained in:
Stephane Nicoll
2016-09-13 14:34:10 +02:00
parent dd0442b5de
commit 61cc7eb41d
9 changed files with 100 additions and 40 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,9 @@ package io.spring.initializr.generator
import io.spring.initializr.metadata.InitializrMetadata
import io.spring.initializr.metadata.Type
import io.spring.initializr.util.GroovyTemplate
import io.spring.initializr.util.VersionRange
import static io.spring.initializr.util.GroovyTemplate.template
/**
* Generate help pages for command-line clients.
*
@@ -39,6 +38,12 @@ class CommandLineHelpGenerator {
=========|_|==============|___/=/_/_/_/
'''
private final GroovyTemplate template
CommandLineHelpGenerator(GroovyTemplate template) {
this.template = template
}
/**
* Generate the capabilities of the service as a generic plain text
* document. Used when no particular agent was detected.
@@ -46,7 +51,7 @@ class CommandLineHelpGenerator {
String generateGenericCapabilities(InitializrMetadata metadata, String serviceUrl) {
def model = initializeCommandLineModel(metadata, serviceUrl)
model['hasExamples'] = false
template 'cli-capabilities.txt', model
template.process 'cli-capabilities.txt', model
}
/**
@@ -55,9 +60,9 @@ class CommandLineHelpGenerator {
*/
String generateCurlCapabilities(InitializrMetadata metadata, String serviceUrl) {
def model = initializeCommandLineModel(metadata, serviceUrl)
model['examples'] = template 'curl-examples.txt', model
model['examples'] = template.process 'curl-examples.txt', model
model['hasExamples'] = true
template 'cli-capabilities.txt', model
template.process 'cli-capabilities.txt', model
}
/**
@@ -66,9 +71,9 @@ class CommandLineHelpGenerator {
*/
String generateHttpieCapabilities(InitializrMetadata metadata, String serviceUrl) {
def model = initializeCommandLineModel(metadata, serviceUrl)
model['examples'] = template 'httpie-examples.txt', model
model['examples'] = template.process 'httpie-examples.txt', model
model['hasExamples'] = true
template 'cli-capabilities.txt', model
template.process 'cli-capabilities.txt', model
}
/**
@@ -78,7 +83,7 @@ class CommandLineHelpGenerator {
String generateSpringBootCliCapabilities(InitializrMetadata metadata, String serviceUrl) {
def model = initializeSpringBootCliModel(metadata, serviceUrl)
model['hasExamples'] = false
template('boot-cli-capabilities.txt', model)
template.process('boot-cli-capabilities.txt', model)
}
protected Map initializeCommandLineModel(InitializrMetadata metadata, serviceUrl) {

View File

@@ -20,6 +20,7 @@ import groovy.util.logging.Slf4j
import io.spring.initializr.InitializrException
import io.spring.initializr.metadata.Dependency
import io.spring.initializr.metadata.InitializrMetadataProvider
import io.spring.initializr.util.GroovyTemplate
import io.spring.initializr.util.Version
import org.springframework.beans.factory.annotation.Autowired
@@ -28,7 +29,6 @@ import org.springframework.context.ApplicationEventPublisher
import org.springframework.util.Assert
import static io.spring.initializr.metadata.InitializrConfiguration.Env.Maven.ParentPom
import static io.spring.initializr.util.GroovyTemplate.template
/**
* Generate a project based on the configured metadata.
@@ -58,6 +58,9 @@ class ProjectGenerator {
@Autowired
ProjectRequestResolver requestResolver
@Autowired
GroovyTemplate groovyTemplate = new GroovyTemplate()
@Autowired
ProjectResourceLocator projectResourceLocator = new ProjectResourceLocator()
@@ -321,12 +324,12 @@ class ProjectGenerator {
}
private byte[] doGenerateMavenPom(Map model) {
template 'starter-pom.xml', model
groovyTemplate.process 'starter-pom.xml', model
}
private byte[] doGenerateGradleBuild(Map model) {
template 'starter-build.gradle', model
groovyTemplate.process 'starter-build.gradle', model
}
private void writeGradleWrapper(File dir) {
@@ -384,7 +387,7 @@ class ProjectGenerator {
def write(File target, String templateName, def model) {
def tmpl = templateName.endsWith('.groovy') ? templateName + '.tmpl' : templateName
def body = template tmpl, model
def body = groovyTemplate.process tmpl, model
target.write(body)
}

View File

@@ -16,36 +16,52 @@
package io.spring.initializr.util
import java.util.concurrent.ConcurrentMap
import groovy.text.GStringTemplateEngine
import groovy.text.Template
import groovy.text.TemplateEngine
import org.codehaus.groovy.control.CompilationFailedException
import org.springframework.util.ConcurrentReferenceHashMap
/**
* @author Dave Syer
* @since 1.0
*/
class GroovyTemplate {
// This is a copy/paste from GroovyTemplate in spring-boot-cli. We should migrate
// to Spring's native support available in 4.1
boolean cache = true
static String template(String name, Map<String, ?> model) throws IOException,
CompilationFailedException, ClassNotFoundException {
template(new GStringTemplateEngine(), name, model)
private final TemplateEngine engine
private final ConcurrentMap<String, Template> templateCaches = new ConcurrentReferenceHashMap<>()
GroovyTemplate(TemplateEngine engine) {
this.engine = engine
}
static String template(TemplateEngine engine, String name, Map<String, ?> model)
GroovyTemplate() {
this(new GStringTemplateEngine())
}
String process(String name, Map<String, ?> model)
throws IOException, CompilationFailedException, ClassNotFoundException {
def writable = getTemplate(engine, name).make(model)
def template = getTemplate(name)
def writable = template.make(model)
def result = new StringWriter()
writable.writeTo(result)
result.toString()
}
static Template getTemplate(TemplateEngine engine, String name)
Template getTemplate(String name)
throws CompilationFailedException, ClassNotFoundException, IOException {
if (cache) {
return this.templateCaches.computeIfAbsent(name, { n -> loadTemplate(n) })
}
return loadTemplate(name)
}
protected Template loadTemplate(String name) {
def file = new File("templates", name)
if (file.exists()) {
return engine.createTemplate(file)
@@ -59,4 +75,5 @@ class GroovyTemplate {
return engine.createTemplate(name)
}
}

View File

@@ -19,6 +19,7 @@ package io.spring.initializr.generator
import io.spring.initializr.metadata.Dependency
import io.spring.initializr.metadata.Type
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
import io.spring.initializr.util.GroovyTemplate
import org.junit.Test
import static org.hamcrest.CoreMatchers.containsString
@@ -30,7 +31,7 @@ import static org.junit.Assert.assertThat
*/
class CommandLineHelpGeneratorTests {
private CommandLineHelpGenerator generator = new CommandLineHelpGenerator()
private CommandLineHelpGenerator generator = new CommandLineHelpGenerator(new GroovyTemplate())
@Test
void generateGenericCapabilities() {