Groovy:
1. 是基于 JVM 的敏捷的动态语言,它可以使用其他Java语言编写的库,特别适合与Spring的动态语言支持一起使用。
2. 它既可以用于面相对象编程,又可以用作纯粹的脚本语言
3. 具有闭包和动态语言中的其它特性
Groovy 模版引擎:
1. Groovy 的模板引擎框架简化视图编程
(1) Groovy 模板引擎与 XSLT 很类似,可以产生模板定义的任何格式,包括 XML、HTML、SQL 和 Groovy 代码。
(2) Groovy 支持 here-docs 的概念,here-doc是创建格式化 String
(例如 HTML 和 XML)的一种便利机制。注意 here-doc 语法与普通的 String
声明并没有很大的不同,不过三重引号。
清单 2. Groovy 中的 Here-docs
itext = """ This is another multiline String that takes up a few lines. Doesn't do anything different from the previous one. """
(3) Groovy 使用 GString
来简化运行时替换。简单来说,GString
允许您使用 与 bash 类似的${}
语法进行替换。GString
的优势是您从来都不需要知道自己正在使用的是 GString
类型;只需要在 Groovy 中简单地编写 String
即可,就仿佛是在 Java 代码中一样。
清单 3. Groovy 中的 GString
lang = "Groovy" println "Uncle man, Uncle man, I dig ${lang}."
运行时替换实际上是动态语言的一个通用特性;与其他情况一样,Groovy 还会更进一步。Groovy 的 GString
允许您对替换的值调用 autocall方法,当开始构建动态文本时,这会产生很多变化。例如,在清单 4 中,我可以对指定的变量按照 String
对象类型调用一个方法(在本例中是 length()
方法)。
清单 4. GString 自动调用
lang = "Groovy" println "I dig any language with ${lang.length()} characters in its name!"
(4)Groovy 模版
使用模版 = 创建模板+提供映射。
创建这些模板的关键在于对那些运行时要替换的变量的定义。例如,在清单 5 中,我们为创建 GroovyTestCase
定义了一个模板。
清单 5. 一个创建 GroovyTestCase 的模板
import groovy.util.GroovyTestCase class <%=test_suite %> extends GroovyTestCase { <% for(tc in test_cases) { println "\tvoid ${tc}() { } " }%> }
清单 5 中的模板就类似于一个 JSP 文件,因为我们使用了 <%
和 <%=
语法。然而,由于 Groovy 的灵活性很好,因此您并不局限于使用 JSP 语法。您还可以自由使用 Groovy 中杰出的 GString
,如清单 6 所示。
清单 6. GString 的使用
<person> <name first="${p.fname}" last="${p.lname}"/> </person>
在清单 6 中,我创建了一个简单的模板,它表示一个定义 person
元素集合的 XML 文档。您可以看到这个模板期望一个具有 fname
和 lname
属性、名为 p
的对象。Groovy 是一种简化从模型中分离视图过程的手段。
下一个步骤是编写运行时的映射代码。
我所需要的是一个 映射
,其关键字是模板中的变量名,键值是运行时的值。
例如,如果一个简单的模板有一个名为 favlang
的变量,我就需要与 favlang
键值建立一个 映射
。这个键值可以是根据我自己的喜好选择的任何脚本语言(在本例中,当然是 Groovy)。
在清单 7 中,我们定义了这样一个简单的模板,在清单 8 中,我将向您展示对应的映射代码。
清单 7. 用来展示映射的简单代码
My favorite dynamic language is ${favlang}
清单 8. 为一个简单的模板映射值
package com.vanward.groovy.tmpl import groovy.text.Template import groovy.text.SimpleTemplateEngine import java.io.File class SimpleTemplate{ static void main(args) { fle = new File("simple-txt.tmpl") binding = ["favlang": "Groovy"] engine = new SimpleTemplateEngine() template = engine.createTemplate(fle).make(binding) println template.toString() } }
这段代码中创建了一个 binding
对象;实际上,这就是一个 映射
。我将在模板中找到的值 favlang
映射到 String
Groovy 上。
清单 8 中的最后一个步骤是打印进程的输出信息。正如您可以看到的一样,创建一个 binding
对象并提供正确的映射是件轻而易举的小事,至少在我们这个简单的例子中是如此。在下一节中,我们将会使用一个更加复杂的例子对 Groovy 模板引擎进行测试。
在清单 9 中,创建了一个 Person
类来表示在 清单 6中定义的 person
元素。
清单 9. Groovy 中的 Person 类
class Person{ age fname lname String toString(){ return "Age: " + age + " First Name: " + fname + " Last Name: " + lname } }
在清单 10 中,您可以看到对上面定义的 Person
类的实例进行映射的代码。
清单 10. 在 Person 类与模板之间建立映射
import java.io.File import groovy.text.Template import groovy.text.SimpleTemplateEngine class TemplatePerson{ static void main(args) { pers1 = new Person(age:12, fname:"Sam", lname:"Covery") fle = new File("person_report.tmpl") binding = ["p":pers1] engine = new SimpleTemplateEngine() template = engine.createTemplate(fle).make(binding) println template.toString() } }
上面的代码看起来很熟悉,不是吗?实际上,它与 清单 8非常类似,不过增加了一行创建 pers1
实例的代码。现在,再次快速查看一下 清单 6中的代码。您看到模板是如何引用属性 fname
和 lname
的了吗?我所做的操作是创建一个 Person
实例,其 fname
属性设置为“Sam”,属性 lname
设置为“Covery”。
在运行清单 10 中的代码时,输出结果是 XML 文件,用来定义 person
元素,如清单 11 所示。
清单 11. Person 模板的输出结果
<person> <name first="Sam" last="Covery"/> </person>
映射一个列表
在 清单 5中,我为 GroovyTestCase
定义了一个模板。现在如果您看一下这个模板,就会注意到这个定义有一些逻辑用于在一个集合上迭代。在清单 12 中,您将看到一些非常类似的代码,不过这些代码的逻辑是用来映射一个测试用例 列表
的。
清单 12. 映射测试用例列表
fle = new File("unit_test.tmpl") coll = ["testBinding", "testToString", "testAdd"] binding = ["test_suite":"TemplateTest", "test_cases":coll] engine = new SimpleTemplateEngine() template = engine.createTemplate(fle).make(binding) println template.toString()
查看一下 清单 5,它显示了模板期望一个名为“test_cases”的 列表
—— 在清单 12 中它定义为 coll
,包含 3 个元素。我简单地将 coll
设置为“test_cases”绑定对象中的键值,现在代码就准备好运行了。
现在我将开始定义模板了 —— 它看起来更加类似于想要的输出结果,采用了一些逻辑来循环遍历一组类。
清单 14. 为原来的代码应用模板
<md5report> <% for(clzz in clazzes) { println "<md5 class=\"${clzz.name}\" value=\"${clzz.value}\"/>" }%> </md5report>
清单 14 中定义的模板与为 GroovyTestCase
定义的模板类似,其中包括循环遍历一个集合的逻辑。还要注意我在此处混合使用了 JSP 和 GString
的语法。
编写映射代码
定义好模板之后,下一个步骤是编写运行时的映射代码。我需要将原来的写入文件的逻辑替换为下面的代码:构建一个 ChecksumClass
对象集合,然后将这些对象放到 binding
对象中。
这个模型然后就会变成清单 15 中定义的 ChecksumClass
。
清单 15. 在 Groovy 中定义的 CheckSumClass
class CheckSumClass{ name value String toString(){ return "name " + name + " value " + value } }
创建集合
接下来,我需要重构刚才写入文件的那段代码 —— 这一次采用一定的逻辑使用 ChecksumClass
构造一个列表,如清单 16 所示。
清单 16. 重构代码创建一个 ChecksumClass 的集合
clssez = [] for(f in scanner){ f.eachLine{ line | iname = formatClassName(bsedir, f.path) clssez << new CheckSumClass(name:iname, value:line) } }
清单 16 显示了使用类 Ruby 的语法将对象添加到 列表
中是多么简单 —— 这就是 奇妙的groovy。我首先使用 []
语法创建 清单
。然后使用简短的 for
循环,后面是一个带有闭包的迭代器。这个闭包接受每一个 line
(在本例中是一个校验和值),并创建一个新定义的 CheckSumClass
实例(使用 Groovy 的自动生成的构造函数),并将二者添加到集合中。
添加模板映射
我需要做的最后一件事情是添加模板引擎特定的代码。这段代码将执行运行时映射,并将对应的格式化后的模板写入原始的文件中,如清单 17 所示。
清单 17. 使用模板映射重构原来的代码
fle = new File("report.tmpl") binding = ["clazzes": clzzez] engine = new SimpleTemplateEngine() template = engine.createTemplate(fle).make(binding) nfile.withPrintWriter{ pwriter | pwriter.println template.toString() }
现在,清单 17 中的代码对您来说太陈旧了。我利用了清单 16 中的 列表
,并将其放入 binding
对象。然后读取 nfile
对象,并将相应的输出内容从 清单 14中的映射模板写入文件中。
在将这些内容都放入清单 18 之前,您可能希望返回 清单 13最后看一眼开始时使用的那段蹩脚的代码。下面是新的代码,您可以进行比较一下:
清单 18. 看,新的代码!
/** * */ buildReport(bsedir){ ant = new AntBuilder() scanner = ant.fileScanner { fileset(dir:bsedir) { include(name:"**/*class.md5.txt") } } rdir = bsedir + File.separator + "xml" + File.separator file = new File(rdir) if(!file.exists()){ ant.mkdir(dir:rdir) } nfile = new File(rdir + File.separator + "checksum.xml") clssez = [] for(f in scanner){ f.eachLine{ line | iname = formatClassName(bsedir, f.path) clssez << new CheckSumClass(name:iname, value:line) } } fle = new File("report.tmpl") binding = ["clazzes": clzzez] engine = new SimpleTemplateEngine() template = engine.createTemplate(fle).make(binding) nfile.withPrintWriter{ pwriter | pwriter.println template.toString() } }
回顾一下,我所干的事情不过是将一些蹩脚的 println
替换成 Groovy 的更精巧的模板代码。(一些熟悉重构的人可能会说我应该使用 Extract Method进一步对代码进行优化。)