`

实战 Groovy: 用 Groovy 进行 Ant 脚本编程

阅读更多

Ant 和 Maven 两者在构建处理工具的世界中占统治地位。但是 XML 却凑巧是一种非常没有表现力的配置格式。在“实战 Groovy”这个新系列的第 2 期中,Andrew Glover 将介绍 Groovy 的生成器实用工具,这个工具能够极其容易地把 Groovy 与 Ant 和 Maven 结合在一起,形成更具表现力、更可控的构建。

Ant 作为 Java 项目构建工具的普遍性和实用性是无法超越的。即使是 Maven 这个构建领域的新锐工具,也要把自己的许多强大能力归功于从 Ant 学到的经验。但是,这两个工具有共同的不足之处:扩展性。即使 XML 的可移植性在促进 Ant 和 Maven 走向开发前端上扮演了主要角色,XML 作为构建配置格式的角色仍然或多或少地限制了构建过程的表现力。

例如,虽然在 Ant 和 Maven 中都有条件逻辑,但是用 XML 表示有些繁琐。而且,虽然可以定义定制任务来扩展 Ant 的构建过程,但是这么做通常会把应用程序的行为局限在有限的沙箱中。因此,在本文中,我将向您演示如何把 Groovy 和 Ant 在 Maven 内部 结合起来,形成更强大的表现力,从而对构建过程进行更好的行为控制。

很快您自己就会看到,Groovy 给 Ant 和 Maven 都带来了令人瞩目的增强,Groovy 的优点在于它常常能恰到好处地弥补 XML 的遗漏!实际上,看起来 Groovy 的创建者肯定也曾经历过用 XML 实施 Ant 和 Maven 构建过程的痛苦,因为他们引入了 AntBuilder,这是一个强大的新工具,支持在 Groovy 脚本中利用 Ant。

在 实战 Groovy 的这一期中,我将向您展示不用 XML,转用 Groovy 作为您的构建配置格式,对构造过程进行增强是多么容易。闭包(closure)是 Groovy 的一个重要特性,而且是这门语言之所以与众不同的核心,所以我在转到下一节之前,先要对闭包做一个快速回顾。

快速回顾闭包

就像 Groovy 自己的一些非常著名的祖先一样,Groovy 也支持 匿名函数 或 闭包(closure) 的概念。如果您曾经用 Python 或 Jython 编写过代码,那么您可能对闭包比较熟悉,在这两种语言中,都是用 lambda 关键字引入闭包。在 Ruby 中,如果 没有 利用块或闭包,那么您就不得不实际地编写脚本。即使是 Java 语言,也要通过它的 匿名内部类,对匿名函数提供了有限形式的支持。

在 Groovy 中,闭包是能够封装行为的第一级匿名函数。当 Ruby 的缔造者 Yukihiro Matsumoto 注意到这些强大的第一类对象可以“传递给另一个函数,然后函数可以调用传入的 [闭包]”时,也开始染指闭包的应用(请参阅 参考资料,查看完整的采访)。当然,亲自操作 Groovy 是了解闭包对于这门美好的令人激动的语言是一笔多么大的财富的最好方法。


 

闭包实例

在清单 1 中,我又使用了一些本系列的第 1 篇文章曾经用过的代码;但是这一次的重点是闭包。如果您已经读过那篇文章(请参阅 参考资料),那么您可以回忆起我用来演示用 Groovy 进行单元测试的基于 Java 的包过滤对象。这次,我还用相同的示例开始,但是用闭包对它进行了极大的增强。下面是基于 Java 的 Filter 接口。


清单 1. 还记得这个简单的 Java Filter 接口吗?

public interface Filter {
  void setFilter(String fltr);  
  boolean applyFilter(String value);
}

 

上次,在定义了 Filter 类型之后,我接着定义了两个实现,这两个实现名为 RegexPackageFilter 和 SimplePackageFilter,它们分别使用了正则表达式和简单的 String 操作。

对于没用闭包写成的代码来说,到现在为止还算不错。在清单 2 中,您会开始看到只有一点语法上的变化,代码就不同了(更好了!)。我将通过定义通用的 Filter 类型(如下所示)开始,但是这次是用 Groovy 定义的。请注意与 Filter 类关联的 strategy 属性。这个属性是闭包的一个实例,在执行 applyFilter 方法的时候可以调用它。


清单 2. 更加 Groovy 的过滤器 —— 使用闭包

class Filter{
   strategy
   boolean applyFilter(str){
      return strategy.call(str)
   }
}

 

添加闭包意味着我不用像在原来的 Filter 接口中那样必须定义一个接口类型,或者依赖特定的实现才能得到期望的行为。相反,我可以定义一个通用的 Filter 类型,并给它提供一个闭包,让它在执行applyFilter 方法期间应用。

下一步是定义两个闭包。第一个通过对指定参数应用简单的 String 操作来模拟 SimplePackageFilter(来自前一篇文章)。当创建新的 Filter 类型时,对应的名为 simplefilter 的闭包会被传递到构造器中。第二个闭包(在几个进行代码验证的 assert 之后出现)对指定的 String 应用正则表达式。这次我还是要创建一个新的 Filter 类型,向正则表达式传递一个名为 rfilter 的闭包,并执行一些 assert,以确保每件事都正常。所有细节如清单 3 所示:


清单 3. 使用 Groovy 闭包的简单魔术

simplefilter = { str | 
   if(str.indexOf("java.") >= 0){
     return true
   }else{
     return false
   }
}
		
fltr = new Filter(strategy:simplefilter)
assert !fltr.apply("test")
assert fltr.apply("java.lang.String")
		
rfilter = { istr |
   if(istr =~ "com.vanward.*"){
     return true
   }else{
     return false
   }
}
		
rfltr = new Filter(strategy:rfilter)
assert !rfltr.apply("java.lang.String")
assert rfltr.apply("com.vanward.sedona.package") 

 

非常令人难忘,对吧?使用闭包,我能够把对期望行为的定义推迟到运行时 —— 所以不必像在以前的设计中要做的那样再定义新的 Filter 类型并编译它。虽然我用 Java 代码时能用匿名内部类做类似的事情,但是用 Groovy 更容易、神秘性更少一些。

闭包确实是非常强大的家伙。它们还代表处理行为的另外一种方式 —— 而 Groovy(以及它的远亲 Ruby)对它有非常严重的依赖。所以闭包会是我们下一个主题的要点,下一个主题是用生成器进行构建。

 

用生成器进行构建

使 Groovy 中的 Ant 更迷人的核心之处是 生成器。实际上,生成器允许您很方便地在 Groovy 中表示树形数据结构,例如 XML 文档。而且,女士们先生们请看,秘密在这:使用生成器,特别是AntBuilder,您可以毫不费力地构造 Ant 的 XML 构建文件,不必处理 XML 就可以 执行生成的行为。而这并不是在 Groovy 中使用 Ant 的惟一优点。与 XML 不同,Groovy 是非常有表现力的开发环境,在这个环境中,您可以容易地编写循环结构、条件选择代码,甚至可以利用“重用”的威力,而不必像以前那样,费力地用剪切-粘贴操作来创建新 build.xml 文件。而且您做这些工作时,完全是在 Java 平台中!

生成器的优点,尤其是 Groovy 的 AntBuilder,在于它们的语法表示完全体现了它们所代表的 XML 文件的逻辑进程。被附加在 AntBuilder 实例上的方法与对应的 Ant 任务匹配;同样的,这些方法可以接收参数(以 map 的形式),参数对应着任务的属性。而且,嵌套标签(例如 include 和 fileset)也定义成闭包。

构建块:示例 1

我要用一个超级简单的示例向您介绍生成器:一个叫做 echo 的 Ant 任务。在清单 4 中,我创建了一个普通的、每天都会用到的 Ant 的 echo 标记的 XML 版本(用在这不要奇怪):


清单 4. Ant 的 Echo 任务

<echo message="This was set via the message attribute"/>
<echo>Hello World!</echo>

 

事情在清单 5 中变得更有意思了:我用相同的 Ant 标签,并在 Groovy 中用 AntBuilder 类型重新定义了它。注意,我可以使用 echo 的属性 message,也可以只传递一个期望的 String


清单 5. 用 Groovy 表示的 Ant 的 Echo 任务

ant = new AntBuilder()
ant.echo(message:"mapping it via attribute!")		 
ant.echo("Hello World!")

 

生成器特别吸引人的地方是它可以让我把普通的 Groovy 特性与生成器语法混合,从而创建丰富的行为集。在清单 6 中,您应当开始看出可能性是无穷的:


清单 6. 用 Groovy 和 Ant 进行流控制(flow control)

ant = new AntBuilder()
ant.mkdir(dir:"/dev/projects/ighr/binaries/")
try{
    ant.javac(srcdir:"/dev/projects/ighr/src", 
       destdir:"/dev/projects/ighr/binaries/" )
}catch(Throwable thr){
    ant.mail(mailhost:"mail.anywhere.com", subject:"build failure"){
       from(address:"buildmaster@anywhere.com", name:"buildmaster")
       to(address:"dev-team@anywhere.com", name:"Development Team")
       message("Unable to compile ighr's source.")
    }
}

 

在这个示例中,我要捕获源代码编译时的错误条件。注意 catch 块中定义的 mail 对象如何接受定义了 from、 to 和 message 属性的闭包。

哎呀!Groovy 的功能真多!当然,知道什么时候应用这么聪明的特性是问题的关键,而我们都在不断地为之努力。幸运的是,实践出真知,一旦您开始使用 Groovy(或者为了这个原因使用任何脚本语言),您就会找到许多在实际工作中使用它的机会;从中了解它在哪里才真正适用。在下一节中,我将查看一个典型的、现实的示例,并在其中使用一些在这里介绍的特性。

 

应用 Groovy

对于这个示例,我们假设需要为我的代码定期建立校验和(checksum)报告。一旦实现了这个报告,就可以用它在我需要的时候检验文件的完整性。下面是校验和报告的高级技术用例:

  1. 编译所有的源代码。
  2. 对二进制类文件运行 md5 算法。
  3. 创建简单的报告,列出每个类文件及其对应的校验和。

完全摈弃 Ant 或 Maven,整个构建过程都使用 Groovy,在这个例子中,这样做有点极端。但实际上,正如我前面解释的,Groovy 是这些工具的极大 增强,而 不是 替代。所以,用 Groovy 的表现力处理后两项任务,而把第一步信托给 Ant 或 Maven,这样做才有意义。

实际上,我们只要假设我在第一步中用的是 Maven,因为坦白地说,它是我个人偏爱的构建平台。在 Maven 中可以很容易地用 java:compile 和 test:compile 目标编译源文件;所以我保持这部分内容原封不动,让我的新目标引用前面的目标,把前面的目标作为前提条件。实际就是这样 —— 使用编译好的源文件,我准备继续运行校验和工具(checksum utility);但是首先还需要做一些简单的设置。

 

设置 Md5ReportBuilder

为了通过 Ant 运行这个漂亮的校验和工具,我需要两条信息:哪个目录包含要进行校验和处理的文件,报告要写到哪个目录。对于工具的第一个参数,我希望它是一个用逗号分隔的目录列表。对后一个参数,我希望是报告目录。

我决定调用工作类 Md5ReportBuilder。清单 7 中定义了它的 main 方法:


清单 7. Md5ReportBuilder 的 Main 方法

static void main(args) {	 	
		
  assert args[0] && args[1] != null
		
  dirs = args[0].split(",")
  todir = args[1]
		
  report = new Md5ReportBuilder()
  report.runCheckSum(dirs)  
  report.buildReport(todir)		 	
}

 

上述步骤中的第一步是检查这两个参数是不是传递到工具中。然后我把第一个参数用逗号进行分割,创建了一个数组,保存要在上面运行 Ant 的 checksum 任务的目录。最后,我创建 Md5ReportBuilder 类的新实例,调用两个方法处理所需要的功能。

 

添加校验和

Ant 包含一个 checksum 任务,调用起来非常容易,但需要传递一个 fileset 给它,其中包含目标文件的集合。在这个例子中,目标文件是包含编译后的源文件的目录,以及对应的单元测试文件。我可以通过使用 for 循环中的迭代得到这些文件,在这个例子中,是在目录集合中进行迭代。对于每个目录,调用 checksum 任务;而且 checksum 任务只在 .class 文件上运行,如清单 8 所示:


清单 8. runCheckSum 方法

/**
 * runs checksum task for each dir in collection passed in
 */
runCheckSum(dirs){
  ant = new AntBuilder()	   
  for(idir in dirs){	   
    ant.checksum(fileext:".md5.txt" ){
      fileset(dir:idir) {
        include(name:"**/*.class")        
      }
   }
 }
}


构建报告

从这一点起,构建报告就变成了循环练习。读取每个新生成的校验和文件,把该文件对应的信息送到 PrintWriter 方法,然后将 XML 写入文件 —— 虽然用的是最丑陋的形式,如清单 9 所示:


清单 9. 构建报告

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")
  nfile.withPrintWriter{ pwriter |
     pwriter.println("<md5report>")
     for(f in scanner){
       f.eachLine{ line |
         pwriter.println("<md5 class='" + f.path + "' value='" + line + "'/>")
       }
     }
     pwriter.println("</md5report>")
  }		
}	

 

那么,如何处理这个报告呢?首先,我用 FileScanner 找到清单 8 中的 checksum 方法创建的每个校验和文件。然后,我创建一个新目录,在这个目录中再创建一个新文件。(如果我能通过一个简单的 if 语言检查目录是否已经存在,会不会更好一些?)然后我打开对应的文件,并用一个漂亮的闭包从 scanner 集合读取每个对应的 File。示例最后用写入 XML 元素的报告内容进行总结。

我敢打赌您立刻就注意到在 File 的那些实例上的 withPrintWriter 方法是多么强大。我不需要考虑异常或关闭文件,因为它替我处理了每件事。我只是把我希望的行为通过闭包传递给它,然后就准备就绪了!

 

运行工具

这个 Groovy 巡演中的下一站是把它装配进构建过程,特别是放在我的 maven.xml 文件中。非常幸运的是,这是这个相对容易的练习中最容易的部分,如清单 10 所示:


清单 10. 在 Maven 中运行 Md5ReportBuilder

<goal name="gmd5:run" prereqs="java:compile,test:compile">
  <path id="groovy.classpath">						
    <ant:pathelement path="${plugin.getDependencyClasspath()}"/>
    <ant:pathelement location="${plugin.dir}"/>
    <ant:pathelement location="${plugin.resources}"/>            
  </path>
  <java classname="groovy.lang.GroovyShell" fork="yes">
    <classpath refid="groovy.classpath"/>
    <arg value="${plugin.dir}/src/groovy/com/vanward/md5builder/Md5ReportBuilder.groovy"/>
    <arg value="${maven.test.dest},${maven.build.dest}"/>
    <arg value="${maven.build.dir}/md5-report"/>
  </java>
</goal>

 

正如前面所解释过的,我已经规定必须在完整的编译之前运行校验和工具,所以,我的目标有两个前提条件: java:compile 和 test:compile。类路径 Classpath 总是很重要,所以我专门为了让 Groovy 运行特别创建了一个正确的 classpath。最后清单 10 所示的目标调用 Groovy 的外壳,把要运行的脚本和脚本对应的两个参数传给外壳 —— 第一个是要在其上运行校验和(用逗号分隔的)目录,第二个是报告要写入的目录。

 

最后一步

完成对 maven.xml 文件的编码之后,就差不多完成了所有要做的事,但是还有最后一步:我需要用所需的依赖关系来更新 project.xml 文件。没什么好奇怪的,Maven 需要具有一些必要的 Groovy 依赖项才能工作。这些依赖项是汇编代码、字节码操纵库、公共命令行(commons-cli-处理命令行解析)Ant、以及对应的 ant 启动器。这个示例的依赖项如清单 11 所示:


清单 11. Groovy 必需的依赖项

<dependencies>
  <dependency>
    <groupId>groovy</groupId>
    <id>groovy</id>
    <version>1.0-beta-6</version>
  </dependency>
  <dependency>
    <groupId>asm</groupId>
    <id>asm</id>
    <version>1.4.1</version>
  </dependency>
  <dependency>
    <id>commons-cli</id>
    <version>1.0</version>
  </dependency>
  <dependency>
    <groupId>ant</groupId>
    <artifactId>ant</artifactId>
    <version>1.6.1</version>
  </dependency>
  <dependency>
    <groupId>ant</groupId>
    <artifactId>ant-launcher</artifactId>
    <version>1.6.1</version>
  </dependency>
</dependencies>


结束语

在 实战 Groovy 的第 2 期中,您看到了 Groovy 的表现力和灵活性与 Ant 和 Maven 无与伦比的应用结合在一起时发生了什么。对于任何一个工具,Groovy 都提供了有吸引力的替代 XML 的构建格式。它让您用循环构造和条件逻辑控制程序流,极大地增强了构建过程。

在向您展示 Groovy 的实用一面的同时,这个系列还专门介绍了它最适当的应用。应用任何技术的要点之一是认真思考它要应用的环境。在这个案例中,我向您展示了如何用 Groovy 增强 而不是 替换 已经非常强大的工具:Ant。

下个月,我要介绍 Groovy 的另外一项依赖闭包的特性。GroovySql 是个超级方便的小工具,可以使数据库查询、更新、插入以及所有对应的逻辑管理起来特别容易!下期再见!

 

分享到:
评论

相关推荐

    IBM 实战 Groovy

    2. **Ant脚本编程**: Groovy可以用来编写Ant构建脚本,简化构建过程并提高效率。 3. **JDBC编程**: Groovy提供了一种更加灵活的方式来处理数据库操作,比如查询和事务管理。 4. **MVC编程**: Groovy可以用于实现MVC...

    apache-groovy-sdk-3.0.6.zip

    4. **Groovy Ant任务**:Groovy SDK包含了一些Ant任务,允许你在Ant构建脚本中直接使用Groovy,比如运行Groovy脚本、编译Groovy源代码等。 5. **Groovy类库**:Groovy标准库提供了一系列的类和模块,涵盖了集合操作...

    groovy-sdk-4.0.3

    使用Groovy SDK可以进行以下操作: - **开发脚本**:Groovy的简洁语法使得编写脚本变得简单,尤其适合自动化任务、配置管理、测试脚本等场景。 - **创建独立应用**:Groovy可以用来开发完整的应用程序,利用其动态...

    Groovy学习笔记

    - **Ant脚本化**: 可以使用Groovy编写Ant构建脚本。 - **正则表达式**: 支持简洁的正则表达式语法。 - **操作符重载**: 改善了对集合和映射的操作。 - **多重迭代和自动装箱**:简化了循环和数据类型的转换过程。 - ...

    groovy速查手册

    同时,Groovy支持定义脚本并使用`GroovyShell`进行解析和执行。 #### 八、Groovy安装与使用 - **安装**:从官网http://groovy.codehaus.org下载Groovy后,将获得以下可用命令: - `groovy`: 执行Groovy脚本或...

    apache-groovy-sdk-2.4.4

    5. **Ant任务**:对于使用Apache Ant构建系统的项目,Groovy提供了Ant任务,使得可以在Ant脚本中直接执行Groovy脚本,增强了构建过程的灵活性。 6. **Grails** 支持:尽管Grails是一个独立的Web框架,但Groovy SDK...

    Groovy中文版教程

    8. **Gradle**:构建工具Gradle的默认脚本语言就是Groovy,它结合了Ant的灵活性和Maven的依赖管理,使得构建过程更加高效。 9. **领域特定语言**(DSL):Groovy非常适合构建DSL,因为其语法的灵活性和动态性。许多...

    apache-groovy-sdk-3.0.8.zip

    10. **构建工具**:Groovy也常用于构建工具,如Gradle,它使用Groovy编写构建脚本,相比Ant或Maven,Gradle的脚本更易于理解和修改。 在Apache Groovy SDK 3.0.8的压缩包中,"groovy-3.0.8"目录很可能包含了以下...

    groovy-binary-1.8.6

    - **构建工具**:可能包含构建脚本(如Ant或Gradle),用于构建和打包Groovy项目。 这个版本可能修复了一些已知的bug,优化了性能,或者增加了新的功能和特性。使用这个版本时,开发者需要确保系统中已经安装了对应...

    awesome-groovy:精妙的groovy库,框架和资源的精选列表

    Groovy是一种基于Java平台的动态编程语言,它以其简洁、强大的语法和对Java代码的无缝集成而备受开发者喜爱。在标题和描述中提到的"awesome-groovy"是一个精选列表,收集了各种高质量的Groovy库、框架和资源,旨在...

    Groovy入门经典.pdf

    8. **AntBuilder**:Groovy可以方便地与Ant任务集成,通过AntBuilder来编写构建脚本,比纯XML形式的Ant更易读、易写。 9. **Spock测试框架**:Groovy还有优秀的测试框架Spock,它的语法简洁且富有表达力,使得编写...

    Groovy 学习笔记

    2. **Ant with Groovy**: Groovy可以作为Ant任务的语言,使Ant脚本更加简洁和强大。 **五、Groovy的其他应用** 1. **脚本编写**: Groovy的简洁语法使其成为编写自动化脚本的理想选择,如系统管理、测试和持续集成...

    apache-groovy-sdk-2.5.8.zip

    Apache Groovy SDK 2.5.8 是一个重要的软件开发工具包,专为Groovy编程语言设计。Groovy,作为Gradle的底层语言,对于理解并深入掌握Gradle构建系统至关重要。Groovy是一种动态、类型安全的编程语言,它与Java高度...

    apache-groovy-sdk-2.4.15

    这个SDK包含Groovy语言的完整开发工具包,允许开发者在Java平台上进行Groovy程序的设计、构建、测试和部署。 Groovy是一种静态或动态类型的JVM语言,它的设计目标是提高开发者的生产力,同时保持与Java的兼容性。...

    groovy 1.7官方教程

    - **测试指南**:详细介绍如何使用Groovy进行单元测试、集成测试等。 - **开发者指南**:为开发者提供深入的技术文档,包括API文档、构建过程等。 - **Cookbook**:包含一系列实用的代码片段和示例,帮助开发者快速...

    Groovy User Guide

    - **适用场景:** 当使用 Groovy 编写 Ant 脚本时遇到问题。 4. **构建支持 (Builder Support)** - **内容:** 解释了如何利用 Groovy 的构建者模式来简化 XML 和 HTML 的创建过程。 - **适用场景:** 开发者...

Global site tag (gtag.js) - Google Analytics