Java中使用Groovy的三种方式

/ Java / 没有评论 / 1432浏览

Java中使用Groovy的三种方式

一直想抽些时间回顾一下Groovy,非常感谢Groovy,虽然只使用了其很小一部分功能,却给项目运行带来了极大的便利。该博客用于帮助那些需要在Java中集成Groovy的童鞋们。

一.Groovy简单了解

1.简介

Groovy 是 用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。

Groovy是JVM的一个替代语言(替代是指可以用 Groovy 在Java平台上进行 Java 编程),使用方式基本与使用 Java代码的方式相同,该语言特别适合与Spring的动态语言支持一起使用,设计时充分考虑了Java集成,这使Groovy 与 Java 代码的互操作很容易。(注意:不是指Groovy替代java,而是指Groovy和java很好的结合编程。

有人说,Groovy 是下一代的Java语言,不是说Groovy会替代Java,读者如果看过Java8 之 lambda表达式 与 Stream,不难发现lambda和Groovy中的闭包非常相似,Stream也融入了Groovy的思想,所以Java会越来越像Groovy。

2.基本特性

  1. 构建在强大的Java语言之上 并 添加了从Python,Ruby和Smalltalk等语言中学到的 诸多特征,例如动态类型转换、闭包和元编程(metaprogramming)支持。。
  2. 为Java开发者提供了 现代最流行的编程语言特性,而且学习成本很低(几乎为零)。
  3. 支持DSL(Domain Specific Languages领域定义语言)和其它简洁的语法,让代码变得易于阅读和维护。
  4. 受检查类型异常(Checked Exception)也可以不用捕获。
  5. Groovy拥有处理原生类型,面向对象以及一个Ant DSL,使得创建Shell Scripts变得非常简单。
  6. 在开发Web,GUI,数据库或控制台程序时 通过 减少框架性代码 大大提高了开发者的效率。
  7. 支持单元测试和模拟(对象),可以 简化测试。
  8. 无缝集成 所有已经存在的 Java对象和类库。
  9. 直接编译成Java字节码,这样可以在任何使用Java的地方 使用Groovy。
  10. 支持函数式编程,不需要main函数。
  11. 一些新的运算符。
  12. 默认导入常用的包。
  13. 断言不支持jvm的-ea参数进行开关。
  14. 支持对对象进行布尔求值。
  15. 类不支持default作用域,且默认作用域为public。
  16. groovy中基本类型也是对象,可以直接调用对象的方法。

二.Java中使用Groovy的三种方式

注:该Maven项目需要加入groovy-all包的依赖,这里使用的是最新的2.4.3版本。Eclipse中最好安装groovy的插件,便于调试。

1.使用GroovyShell执行groovy脚本

(1). 通过evaluate方法执行groovy片段

GroovyShell类提供一个evaluate方法,可直接运行一段字符串标示的groovy片段,如

// 调用evaluate方法直接执行一段Groovy
public static void testGroovy1() throws CompilationFailedException, IOException {
  GroovyShell groovyShell = new GroovyShell();
  groovyShell.evaluate("println 'My First Groovy shell.'");
}

运行该方法输出如下:

My First Groovy shell.

是不是很方便,GroovyShell的evaluate方法非常类似于Js的eva方法,可执行一段字符串。

(2).通过evaluate方法调用groovy脚本文件

首先新建一个Groovy文件:GroovyShell_1_1.groovy,里面有一个无参的方法sayHello,并在最后调用该方法。

// 不带参数的groovy方法
def sayHello() {
  println 'Hello World.'

  // 如果不写return, groovy方法的默认最后一行为 方法的返回值
  //return "GroovyShell_1中的sayHello()方法的返回值"
  "GroovyShell_1中的sayHello()方法的返回值"
}

// 运行groovy方法
sayHello()

在Java中就可以直接调用这个groovy文件执行了,方法如下:

// 调用GroovyShell_1_1
public static void testGroovy2() throws CompilationFailedException, IOException {
  GroovyShell groovyShell = new GroovyShell();
  Object result = groovyShell.evaluate(new File("src/main/java/com/juxinli/groovy/GroovyShell_1_1.groovy"));
  logger.info(result.toString());
}

运行结果如下:

Hello World.
2015-06-10 18:23:27 [main] INFO tool.Tool_GroovyShell_1 : GroovyShell_1中的sayHello()方法的返回值

第一行是 方法sayHello输出的,第二行是sayHello方法的返回值. 当然,你可以传一个参数给Groovy文件并执行,新建GroovyShell_1_1.groovy,提供一个传参的sayHello方法,如:

// 带参数的groovy方法
def sayHello(name) {
  println "Hello " + name + "."
  // 如果不写return, groovy方法的默认最后一行为 方法的返回值
  //return "GroovyShell_1中的sayHello()方法的返回值"
  "GroovyShell_1中的sayHello(name)方法的返回值"
}

// 运行groovy方法
sayHello(name)

在Java中使用Groovy提供的Binding类来绑定参数

// 调用GroovyShell_1_2
public static void testGroovy3() throws CompilationFailedException, IOException {
  // 调用带参数的groovy shell时,使用bind绑定数据
  Binding binding = new Binding();
  binding.setProperty("name", "Juxinli");

  GroovyShell groovyShell = new GroovyShell(binding);
  Object result = groovyShell.evaluate(new File("src/main/java/com/juxinli/groovy/GroovyShell_1_2.groovy"));
  logger.info(result.toString());
}

运行结果如下:

Hello Juxinli.
2015-06-10 18:30:01 [main] INFO tool.Tool_GroovyShell_1 : GroovyShell_1中的sayHello(name)方法的返回值

是不是很简单 ,但evaluate方法提供的作用不止这些,查官方API你会发现. evaluate方法还可以执行GroovyCodeSource(Groovy提供的包装类),或者 从互联网上执行一段脚本,还可以从输入流来执行相应的groovy等等。种类很多,大家有兴趣可以去研究。

2. 通过GroovyClassLoader动态加载Groovy Class

我们比较熟悉Java的ClassLoader类加载器,当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。而GroovyClassLoader,顾名思义,就是用来加载Groovy类的加载器,想更深入了解,请参考Groovy深入探索——Groovy的ClassLoader体系

通过GroovyClassLoader执行Groovy的方法如下:

首先新建了Groovy Class

package com.juxinli.groovy
class GroovyShell_2 {
	public String sayHello(String name, String sex, int age) {
		println 'GroovyShell_2 的sayHello(String name, String sex, int age)方法';
		return "name: " + name + ", sex: " + sex + ", age: " + age;
	}
}

在Tool_GroovyShell_2中就可以加载该Groovy Class了

public class Tool_GroovyShell_2 {

  private static Logger logger = Logger.getLogger(Tool_GroovyShell_2.class);

  private static GroovyClassLoader groovyClassLoader = null;

  public static void initGroovyClassLoader() {
    CompilerConfiguration config = new CompilerConfiguration();
    config.setSourceEncoding("UTF-8");
    // 设置该GroovyClassLoader的父ClassLoader为当前线程的加载器(默认)
    groovyClassLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader(), config);
  }

  /**
	 * 通过GroovyClassLoader加载GroovyShell_2,并反射调用其sayHello(String name, String sex, int age)方法
	 * 
	 */
  public static String invokeSayHello(String name, String sex, int age) {
    String result = "";

    File groovyFile = new File("src/main/java/com/juxinli/groovy/GroovyShell_2.groovy");
    if (!groovyFile.exists()) {
      return result;
    }

    try {
      // 获得GroovyShell_2加载后的class
      Class<?> groovyClass = groovyClassLoader.parseClass(groovyFile);
      // 获得GroovyShell_2的实例
      GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
      // 反射调用sayHello方法得到返回值
      Object methodResult = groovyObject.invokeMethod("sayHello", new Object[] {name, sex, age});
      if (methodResult != null) {
        result = methodResult.toString();
      }
    } catch (Exception e) {
      logger.warn("加载groovy类失败", e);
    }
    return result;
  }

  public static void main(String[] args) throws Exception {
    initGroovyClassLoader();
    System.out.println(invokeSayHello("张三", "男", 25));
  }
}

其方式和Java中类的加载反射类似,这里不再熬述。需要注意的是,GroovyClassLoader与Java中的加载器一样,同一个类名的类只能加载一次,如果想再次加载,必须调用GroovyClassLoader的clearCache()方法移除所有已经加载的Groovy Class,详细文档见 Groovy在线文档

运行结果:

GroovyShell_2 的sayHello(String name, String sex, int age)方法
name: 张三, sex: 男, age: 25

第一行为GroovyShell_2.groovy的sayHello(...)方法中内部的输出,第二行为其返回的字符串。

3.使用GroovyScriptEngine脚本引擎加载Groovy脚本

GroovyScriptEngine从指定的位置(文件系统,URL,数据库等等)加载Groovy脚本,并且随着脚本变化可重新加载它们。和GroovyShell一样,GroovyScriptEngine也可以传进变量值返回脚本的计算结果。这样我们可以把一些可用的计算公式或计算条件写入Groovy脚本中来执行应用计算。当这些公式或计算条件变更时,我们可更方便地进行更改计算。

从文件夹中加载Groovy脚本的例子如下:

首先在com.juxinli.groovy.shell包中新建三个groovy script

GroovyShell_3_1.groovy

package com.juxinli.groovy.shell

def sayHello(String name) {
	println "Hello, " + name
	"GroovyShell_3_1中的sayHello()方法的返回值"
}

sayHello(name)

GroovyShell_3_2.groovy

package com.juxinli.groovy.shell

def sayHello(String name) {
	println "你好, " + name
	"GroovyShell_3_2中的sayHello()方法的返回值"
}

sayHello(name)

GroovyShell_3_3.groovy

package com.juxinli.groovy.shell

def sayHello(String name) {
  println "Привет, " + name
  "GroovyShell_3_3中的sayHello()方法的返回值"
}

// 运行groovy方法
sayHello(name)

使用GroovyScriptEngine从com.juxinli.groovy.shell包中加载、运行这些script

public class Tool_GroovyShell_3 {

  public static void main(String[] args) throws Exception {
    // GroovyScriptEngine的根路径,如果参数是字符串数组,说明有多个根路径
    GroovyScriptEngine engine = new GroovyScriptEngine("src/main/java/com/juxinli/groovy/shell/");
    //GroovyScriptEngine engine = new GroovyScriptEngine(new String[] {"src/main/java/com/juxinli/groovy/shell/"});

    Binding binding = new Binding();
    binding.setVariable("name", "juxinli");

    Object result1 = engine.run("GroovyShell_3_1.groovy", binding);
    System.out.println(result1);
    Object result2 = engine.run("GroovyShell_3_2.groovy", binding);
    System.out.println(result2);
    Object result3 = engine.run("GroovyShell_3_3.groovy", binding);
    System.out.println(result3);
  }
}

运行结果:

Hello, juxinli
GroovyShell_3_1中的sayHello()方法的返回值
你好, juxinli
GroovyShell_3_2中的sayHello()方法的返回值
Привет, juxinli
GroovyShell_3_3中的sayHello()方法的返回值

GroovyScriptEngine的构造的方法有很多,可以参考 Groovy在线文档 粗浅的回顾了一下,发现还有很多可以深入的地方,大家有好的 Groovy 应用 与发现,欢迎一起讨论、学习。

附源码下载:

java_groovy项目

csdn下载地址

Java中使用Groovy的三种方式http://download.csdn.net/detail/a906998248/8795483

GitHub下载地址

https://github.com/leonzm/java_groovy.git

参考&引用: