Groovy 综述:Groovy 1.7、Grails 1.2 及 Groovy Eclipse 2.0,增强的依赖管理与语言支持

  • Josh Long
  • 张龙

2010 年 1 月 28 日

话题:Java编程语言语言 & 开发

近日Groovy 1.7 发布了,该版本对语言本身进行了优化,同时增强了程序库。随后,SpringSource又发布了 Groovy Eclipse IDE 2.0,增强了对 Groovy 的支持,提供了调试、更棒的内容辅助以及强大的编辑功能,此举弥补了之前 Eclipse 对 Groovy 差劲的支持。此次发布对 Groovy 语言本身和程序库都进行了大量的更新。Java 使用了匿名内部类,而其他语言则使用了闭包或是方法委托,Groovy 就是其中之一。凭借这些新特性,Groovy 可以轻松集成 Java 中使用了匿名内部类的程序库(如果使用闭包的话则无法实现)。相比于每个匿名内部类都要对应一个类的做法来说,这种方式是个极大的改进,同时也使得代码的可读性更好。但官方警告说,从严格意义上讲,该实现并不兼容于 Java 实现。

Java 中匿名内部类一个令人生厌的地方是如果匿名内部类需要访问外部的某个变量,那么该变量必须为 final。Groovy 则消除了这种限制,在 Groovy 中,匿名内部类可以修改外部的变量值。

为了能更好地说明这一点,让我们通过 Java 和 Groovy 分别编写一个示例。该示例来自于 Groovy 的发布声明。首先看看 Java 版本的:

int counter = 0 ;
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
  @Override
  public void run() {
      counter+=1;
  }
},0);

在该示例中,我们用 Java 创建了一个 Timer 对象,然后创建了一个 TimerTask,后者将会执行 run 方法,我们希望 run 方法执行后会更新 counter 这个整型变量。然而遗憾的是,该程序无法编译通过,因为 counter 变量不是 final 的。编译器不允许 run 方法访问该变量。那么将其设为 final 的吧:

final int counter = 0 ;
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
  @Override
  public void run() {
      counter+=1; // compiler error!
  }
},0);

现在 run 方法可以访问 counter 变量了(仅仅是读取而已),但却无法更新该变量,因为它是 final 的,这又导致了另一个编译错误!那么采取一些间接的办法吧,比如说将该整型变量包装到一个对象中。我们打算使用 java.lang.Integer,但它却是不可变的,由于该变量需要为 final,我们就没办法重置该变量了。只能再去尝试其他办法了。我们可以使用 java.lang.ThreadLocal 存储该 Integer 并对其进行更新,或者是采用 java.util.concurrent.atomic.AtomicInteger。

final AtomicInteger atomicIntegerCounter = new AtomicInteger(0);
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
       atomicIntegerCounter.incrementAndGet();
    }
}, 0);

这么做确实可行,但代价实在太大了吧?本来是个很简单的问题,但却被搞成这么复杂:导入了线程相关的类,同时使用这段代码的用户也需要注意线程问题。

现在,看看如何通过 Groovy 解决这个问题吧:

int counter = 0
Timer timer = new Timer()
timer.schedule(new TimerTask() {
    void run() { 
        counter += 1 
    } 
}, 0) 

代码的运行结果与预期一致,没有编译错误,最重要的是这段代码的可读性非常好:其意图是非常清晰的。

新版 Groovy 也支持内部类。推荐的做法是如果非要使用内部类的话,请使用静态内部类。在内部类中访问外部变量的方式与 Java 略有不同。在 Java 中,内部类可以通过一个隐式引用来访问外部类,该引用是作为构造方法的参数传进去的。这里所说的是实现细节了,我们可以直接访问外部类中的变量,就好像这些变量定义在内部类中一样。在 Groovy 中,我们必须显式传递该引用,但却无须编写上面所说的构造方法,当在外部类中实例化内部类时尽管使用该构造方法就行了。

Groovy 注解的使用范围比 Java 还要广。在 Java 5 注解出来时 Groovy 就已经提供了对注解的支持,而 Groovy 1.7 则将这一支持发扬光大:可以对 import 语句、package 声明及变量声明进行注解。

在 import 语句上放置注解可能没什么用,除非使用Grape(Groovy Adaptable Packaging Engine 或是 Groovy Advanced Packaging Engine)。Grape 好比一个增强的、MavenIvy的代码级实现。其他的构建系统都会将依赖放置到外部的构建文件中(比如 Maven 的 pom.xml 或是 Ant 的 build.xml),而借助于 Grape 则可以通过依赖注解代码。这么做会触发系统下载依赖文件,同时使代码的构建更具可读性。在 Groovy 的旧版本中,注解处于一种很尴尬的地位:Groovy 注解只能用在 Java 5 注解可以使用的地方(比如说类或是方法)而不是更加自然的地方(比如在 import 语句上)。

此次更新包含了更具表达力的断言、借助于语言本身抽象语法树(AST)所实现的编译期元编程特性(这样 Java 中通过字节码程序库所实现的功能也能在 Groovy 中实现了),同时还更新了 Groovy Sql 类以支持批更新和事务。

Grails 1.2也随着 Groovy 1.7 一同发布。Grails 是个 Web 框架,使用 Groovy 语言并基于现有流行的开源组件如 Spring MVC 和 Hibernate 所创建出来的快速开发环境,其风格类似于 Ruby on Rails。最近几年 Grails 得到了迅猛的发展。最新发布的 Grails 首次尝到了 SpringSource、Tomcat 和 SpringSource Tool Suite 团队通力合作所带来的好处。

新版 Grails 具有如下主要特性:与 Spring 的集成更加紧密(包括使用 Spring MVC @Controller 注解)、Groovy Object Relational Mapping(即 GORM,构建在 Hibernate 之上的 ORM 解决方案)工具对具名查询的支持、可插拔的 Web 容器(对 Jetty 与 Tomcat 开箱即用的支持)、Grails 视图层技术(Groovy Server Pages)改进的性能与内存使用量。发布声明提到其吞吐量提高了 2——3 倍。Grails 也支持依赖管理而无论使用的 Groovy 版本是什么,通过 Ivy 机制自动下载依赖。该支持是借助于 DSL(进行依赖声明)实现的。

InfoQ 曾采访过 Groovy 与 Grails 领导 Guillaume Laforge 和 Graeme Rocher以了解 Groovy 1.7 及 Grails1.2 的相关信息。

Groovy 语言及周边的生态系统从 2008 年 SpringSource 收购 G2One 这一事件中获益良多。虽然语言背后的发展动力始终保持在稳定的状态,但工具支持这一块还是空白,尤其是 Eclipse 缺乏对 Groovy 的支持。Groovy Eclipse IDE 2.0的发布改变了这一切。虽然该工具套件还不支持最近发布的 Groovy1.7 编译器版本,但它已经提供了调试功能且兼容于 Eclipse3.5。现在,插件已经能够提供响应迅速的内容辅助特性,支持联合编辑(比如可以在 Groovy 项目中编写 Java 代码)机制、跨语言的重构等等。所有这些新特性几乎都建立在完全重写的 Eclipse 插件的基础之上。请查看 SpringSource 发布声明以了解关于此次发布的更多信息。

查看英文原文:Groovy 1.7, Grails 1.2 and Groovy Eclipse 2.0 Updates Include Dependency Management,Language Support

Java编程语言语言 & 开发