本文参考自Template engines,大部分代码直接引用了文档的内容。
模板引擎介绍
Groovy语言包含了一个模板引擎功能,可以生成各种类型的格式化文件,非常方便。模板引擎有下面几个,它们都实现了Template接口。
- SimpleTemplateEngine - 基本的模板
- StreamingTemplateEngine - 功能和 SimpleTemplateEngine相同,不过支持大于64k的模板
- GStringTemplateEngine - 将模板保存为可写的闭包,在流式场景中很有用
- XmlTemplateEngine - 输出XML文件的模板引擎
- MarkupTemplateEngine - 一个完整的、优化过的模板引擎,可以用于生成HTML等模板
SimpleTemplateEngine
这是最贱的模板引擎, 使用起来也非常简单。直接看Groovy文档的例子吧。可以看到它的模板语法类似JSP标签和EL表达式。
代码语言:javascript复制def text = 'Dear "$firstname $lastname",nSo nice to meet you in <% print city %>.nSee you in ${month},n${signed}'
def binding = ["firstname":"Sam", "lastname":"Pullara", "city":"San Francisco", "month":"December", "signed":"Groovy-Dev"]
def engine = new groovy.text.SimpleTemplateEngine()
def template = engine.createTemplate(text).make(binding)
def result = 'Dear "Sam Pullara",nSo nice to meet you in San Francisco.nSee you in December,nGroovy-Dev'
StreamingTemplateEngine
它和SimpleTemplateEngine类似,只不过能处理比较大的文本。它使用<% %>
、<%= %>
和GString语法。下面的例子同样来自Groovy文档。
def text = '''
Dear <% out.print firstname %> ${lastname},
We <% if (accepted) out.print 'are pleased' else out.print 'regret' %>
to inform you that your paper entitled
'$title' was ${ accepted ? 'accepted' : 'rejected' }.
The conference committee.'''
def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)
def binding = [
firstname : "Grace",
lastname : "Hopper",
accepted : true,
title : 'Groovy for COBOL programmers'
]
String response = template.make(binding)
assert response == '''Dear Grace Hopper,
We are pleased to inform you that your paper entitled
'Groovy for COBOL programmers' was accepted.
The conference committee.'''
GStringTemplateEngine
首先我们先将模板保存为文件。注意在模板中没有使用print方法,而是使用out,这样符合GString的规范。
代码语言:javascript复制Dear "$firstname $lastname",
So nice to meet you in <% out << (city == "New York" ? "\"The Big Apple\"" : city) %>.
See you in ${month},
${signed}
然后读取模板,并使用给定的字符串替换模板。
代码语言:javascript复制def f = new File('test.template')
def engine = new groovy.text.GStringTemplateEngine()
def template = engine.createTemplate(f).make(binding)
println template.toString()
XmlTemplateEngine
XmlTemplateEngine用于生成XML模板。它提供了标准的{expression}和variable语法来插入数据。还提供了<gsp:scriptlet>和<gsp:expression>用来插入代码段和表达式。<>"'这几个符号会在处理模板的时候转义。gsp: tags命名空间的标签会在模板生成的时候移除,其他命名空间则不会移除。
下面是Groovy文档的例子。
代码语言:javascript复制def binding = [firstname: 'Jochen', lastname: 'Theodorou', nickname: 'blackdrag', salutation: 'Dear']
def engine = new groovy.text.XmlTemplateEngine()
def text = '''
<document xmlns:gsp='http://groovy.codehaus.org/2005/gsp' xmlns:foo='baz' type='letter'>
<gsp:scriptlet>def greeting = "${salutation}est"</gsp:scriptlet>
<gsp:expression>greeting</gsp:expression>
<foo:to>$firstname "$nickname" $lastname</foo:to>
How are you today?
</document>
'''
def template = engine.createTemplate(text).make(binding)
println template.toString()
生成的结果如下。
代码语言:javascript复制<document type='letter'>
Dearest
<foo:to xmlns:foo='baz'>
Jochen "blackdrag" Theodorou
</foo:to>
How are you today?
</document>
MarkupTemplateEngine
简介
终于说到重点了。这其实才是本篇文章想要说的重点内容。在官方文档中,前面所有内容才占了所有内容的四分之一。剩下的内容都在这里。
Groovy标记模板引擎的功能很强,完全可以作为一个WEB程序的视图层使用。不过我在实际用的时候发现一点问题,那就是Intellij IDEA没有支持……这就比较蛋疼了,我好像在记事本里写代码一样。这大概就是它最主要的缺点了吧。
这个标记模板引擎主要用来生成类XML的模板,类似HTML等等。但是也可以用来生成所有类型的文档。前面那些模板都是基于字符串的。而这个模板是基于Groovy DSL的(学过Gradle就明白了,它们长得很像)。
先来看看直观的例子吧。假设有下面的模板。
代码语言:javascript复制xmlDeclaration()
cars {
cars.each {
car(make: it.make, model: it.model)
}
}
模型数据是这样的。
代码语言:javascript复制model = [cars: [new Car(make: 'Peugeot', model: '508'), new Car(make: 'Toyota', model: 'Prius')]]
最后生成的文件类似这样。
代码语言:javascript复制<?xml version='1.0'?>
<cars><car make='Peugeot' model='508'/><car make='Toyota' model='Prius'/></cars>
Groovy标记模板引擎提供的功能有强类型的模型检查、代码片和导入、国际化等几个重要功能。
代码格式和方法
标记模板其实是合法的Groovy代码。下面这个代码中其实有很多方法和闭包,看看你能认出几个。其实看了这么多Groovy文章的话,应该很容易猜出来哪些是方法、哪些是参数了。
代码语言:javascript复制yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
head {
meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
title('My page')
}
body {
p('This is an example of HTML contents')
}
}
废话就不说了。说说常用的方法吧。
- yieldUnescaped方法会直接输出给定的语句,不转义其中的字符。
- yield方法和上面相反,会转义特殊字符。
- head这些HTML标签方法会生成对应的标签。
- xmlDeclaration()方法会生成一个标准的XML文档头。
- comment方法生成HTML注释。
- newLine生成一个新行。
包含
模板中还可以包含其他模板。下面是三种包含方式,分别包含另一个模板,不需要转义的文本或者是需要转义的文本。
代码语言:javascript复制include template: 'other_template.tpl'
include unescaped: 'raw.txt'
include escaped: 'to_be_escaped.txt'
还有几个等价的Groovy方法,这些方法主要在模板文件是动态的时候才有用。
代码语言:javascript复制includeGroovy(<name>)
includeEscaped(<name>)
includeUnescaped(<name>)
布局
我们可以编写布局文件。布局文件和其它Groovy模板文件的后缀名都是tpl
。我一开始不知道,结果找不到视图文件,花了不少时间。例如编写下面这样一个文件。
html {
head {
title(title)
}
body {
bodyContents()
}
}
然后将页面写为下面这样。Groovy模板引擎会将布局文件和实际布局结合,生成最终的布局。
代码语言:javascript复制layout 'layout-main.tpl',
title: 'Layout example',
bodyContents: contents { p('This is the body') }
使用模板引擎
使用方法和其他模板引擎差不多。都要创建引擎实例,然后传入模板文件和数据。标记模板引擎还需要一个额外的配置对象。
代码语言:javascript复制TemplateConfiguration config = new TemplateConfiguration();
MarkupTemplateEngine engine = new MarkupTemplateEngine(config);
Template template = engine.createTemplate("p('test template')");
Map<String, Object> model = new HashMap<>();
Writable output = template.make(model);
output.writeTo(writer);
默认情况下生成的模板是部分行的,适合生产环境但是不适合人类阅读。我们可以修改配置来让结果更可读。下面的配置打开了缩进和分行。其他配置请查阅相应文档。
代码语言:javascript复制config.setAutoNewLine(true);
config.setAutoIndent(true);
国际化
我们可以为每个模板文件创建不同区域的版本,这样就可以让程序可以实现国际化功能。例如,原始的文件是file.tpl
,那么法语版本的文件就是file_fr_FR.tpl
,英语版本就是file_en_US.tpl
。在创建模板配置对象的时候,我们可以传递一个Locale
对象作为默认区域使用。
强类型声明
Groovy模板的类型是在运行时解析的,不过我们也可以手动声明数据类型,这样模板执行速度会更快。在modelTypes
中将使用到的模型声明出即可。
modelTypes = {
List<Page> pages
}
pages.each { page ->
p("Page title: $page.title")
p(page.text)
}
Spring集成
Spring对Groovy标记模板提供了支持。只要类路径存在Groovy相关类,就可以配置使用Groovy标记模板。
首先先添加Groovy的依赖。下面是Gradle的依赖配置。
代码语言:javascript复制compile 'org.codehaus.groovy:groovy-all:2.4.9'
然后在Spring配置文件中添加下面的配置。
代码语言:javascript复制<!--Groovy模板引擎-->
<mvc:view-resolvers>
<mvc:groovy/>
</mvc:view-resolvers>
<!--Groovy模板引擎配置-->
<mvc:groovy-configurer auto-indent="true"
cache-templates="false"
resource-loader-path="/WEB-INF/templates"/>
之后,在配置的模板路径下编写*.tpl
格式的模板文件,就可以让Spring正常解析了。