Groovy模板引擎

本文参考自Template engines,大部分代码直接引用了文档的内容。

模板引擎介绍

Groovy语言包含了一个模板引擎功能,可以生成各种类型的格式化文件,非常方便。模板引擎有下面几个,它们都实现了Template接口。

  • SimpleTemplateEngine - 基本的模板
  • StreamingTemplateEngine - 功能和 SimpleTemplateEngine相同,不过支持大于64k的模板
  • GStringTemplateEngine - 将模板保存为可写的闭包,在流式场景中很有用
  • XmlTemplateEngine - 输出XML文件的模板引擎
  • MarkupTemplateEngine - 一个完整的、优化过的模板引擎,可以用于生成HTML等模板

SimpleTemplateEngine

这是最贱的模板引擎, 使用起来也非常简单。直接看Groovy文档的例子吧。可以看到它的模板语法类似JSP标签和EL表达式。

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的规范。

Dear "$firstname $lastname",
So nice to meet you in <% out << (city == "New York" ? "\\"The Big Apple\\"" : city) %>.
See you in ${month},
${signed}

然后读取模板,并使用给定的字符串替换模板。

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文档的例子。

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()

生成的结果如下。

<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就明白了,它们长得很像)。

先来看看直观的例子吧。假设有下面的模板。

xmlDeclaration()
cars {
   cars.each {
       car(make: it.make, model: it.model)
   }
}

模型数据是这样的。

model = [cars: [new Car(make: 'Peugeot', model: '508'), new Car(make: 'Toyota', model: 'Prius')]]

最后生成的文件类似这样。

<?xml version='1.0'?>
<cars><car make='Peugeot' model='508'/><car make='Toyota' model='Prius'/></cars>

Groovy标记模板引擎提供的功能有强类型的模型检查、代码片和导入、国际化等几个重要功能。

代码格式和方法

标记模板其实是合法的Groovy代码。下面这个代码中其实有很多方法和闭包,看看你能认出几个。其实看了这么多Groovy文章的话,应该很容易猜出来哪些是方法、哪些是参数了。

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生成一个新行。

包含

模板中还可以包含其他模板。下面是三种包含方式,分别包含另一个模板,不需要转义的文本或者是需要转义的文本。

include template: 'other_template.tpl'
include unescaped: 'raw.txt'
include escaped: 'to_be_escaped.txt'

还有几个等价的Groovy方法,这些方法主要在模板文件是动态的时候才有用。

includeGroovy(<name>) 
includeEscaped(<name>)
includeUnescaped(<name>)

布局

我们可以编写布局文件。布局文件和其它Groovy模板文件的后缀名都是tpl。我一开始不知道,结果找不到视图文件,花了不少时间。例如编写下面这样一个文件。

html {
    head {
        title(title)                
    }
    body {
        bodyContents()              
    }
}

然后将页面写为下面这样。Groovy模板引擎会将布局文件和实际布局结合,生成最终的布局。

layout 'layout-main.tpl',                                   
    title: 'Layout example',                                
    bodyContents: contents { p('This is the body') }  

使用模板引擎

使用方法和其他模板引擎差不多。都要创建引擎实例,然后传入模板文件和数据。标记模板引擎还需要一个额外的配置对象。

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);  

默认情况下生成的模板是部分行的,适合生产环境但是不适合人类阅读。我们可以修改配置来让结果更可读。下面的配置打开了缩进和分行。其他配置请查阅相应文档。

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的依赖配置。

compile 'org.codehaus.groovy:groovy-all:2.4.9'

然后在Spring配置文件中添加下面的配置。

<!--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正常解析了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容