java 嵌入groovy_JAVA嵌入运行Groovy脚本

最近设计一个数据统计系统,系统中上百种数据统计维度,而且这些数据统计的指标可能随时会调整.如果基于java编码的方式逐个实现数据统计的API设计,工作量大而且维护起来成本较高;最终确定为将"数据统计"的计算部分单独分离成脚本文件(javascript,或者Groovy),非常便捷了实现了"数据统计Task" 与 "数据统计规则(计算)"解耦,且可以动态的加载和运行的能力.顺便对JAVA嵌入运行Groovy脚本做个备忘.

Java中运行Groovy,有三种比较常用的类支持:GroovyShell,GroovyClassLoader以及Java-Script引擎(JSR-223).

1) GroovyShell: 通常用来运行"script片段"或者一些零散的表达式(Expression)

2) GroovyClassLoader: 如果脚本是一个完整的文件,特别是有API类型的时候,比如有类似于JAVA的接口,面向对象设计时,通常使用GroovyClassLoader.

3) ScriptEngine: JSR-223应该是推荐的一种使用策略.规范化,而且简便.

一.GroovyShell代码样例

1) 简单的表达式执行,方法调用

/*** 简答脚本执行

*@throwsException*/

public static void evalScriptText() throwsException{//groovy.lang.Binding

Binding binding = newBinding();

GroovyShell shell= newGroovyShell(binding);

binding.setVariable("name", "zhangsan");

shell.evaluate("println 'Hello World! I am ' + name;");//在script中,声明变量,不能使用def,否则scrope不一致.

shell.evaluate("date = new Date();");

Date date= (Date)binding.getVariable("date");

System.out.println("Date:" +date.getTime());//以返回值的方式,获取script内部变量值,或者执行结果//一个shell实例中,所有变量值,将会在此"session"中传递下去."date"可以在此后的script中获取

Long time = (Long)shell.evaluate("def time = date.getTime(); return time;");

System.out.println("Time:" +time);

binding.setVariable("list", new String[]{"A","B","C"});//invoke method

String joinString = (String)shell.evaluate("def call(){return list.join(' - ')};call();");

System.out.println("Array join:" +joinString);

shell= null;

binding= null;

}

2)  伪main方法执行.

/*** 当groovy脚本,为完整类结构时,可以通过执行main方法并传递参数的方式,启动脚本.*/

public static voidevalScriptAsMainMethod(){

String[] args= new String[]{"Zhangsan","10"};//main(String[] args)

Binding binding = newBinding(args);

GroovyShell shell= newGroovyShell(binding);

shell.evaluate("static void main(String[] args){ if(args.length != 2) return;println('Hello,I am ' + args[0] + ',age ' + args[1])}");

shell= null;

binding= null;

}

3)  通过Shell运行具有类结构的Groovy脚本

/*** 运行完整脚本

*@throwsException*/

public static void evalScriptTextFull() throwsException{

StringBuffer buffer= newStringBuffer();//define API

buffer.append("class User{")

.append("String name;Integer age;")//.append("User(String name,Integer age){this.name = name;this.age = age};")

.append("String sayHello(){return 'Hello,I am ' + name + ',age ' + age;}}\n");//Usage

buffer.append("def user = new User(name:'zhangsan',age:1);")

.append("user.sayHello();");//groovy.lang.Binding

Binding binding = newBinding();

GroovyShell shell= newGroovyShell(binding);

String message=(String)shell.evaluate(buffer.toString());

System.out.println(message);//重写main方法,默认执行

String mainMethod = "static void main(String[] args){def user = new User(name:'lisi',age:12);print(user.sayHello());}";

shell.evaluate(mainMethod);

shell= null;

}

4)  方法执行和分部调用

/*** 以面向"过程"的方式运行脚本

*@throwsException*/

public static void evalScript() throwsException{

Binding binding= newBinding();

GroovyShell shell= newGroovyShell(binding);//直接方法调用//shell.parse(new File(//))

Script script = shell.parse("def join(String[] list) {return list.join('--');}");

String joinString= (String)script.invokeMethod("join", new String[]{"A1","B2","C3"});

System.out.println(joinString);脚本可以为任何格式,可以为main方法,也可以为普通方法//1) def call(){...};call();//2) call(){...};

script = shell.parse("static void main(String[] args){i = i * 2;}");

script.setProperty("i", new Integer(10));

script.run();//运行,

System.out.println(script.getProperty("i"));//the same as

System.out.println(script.getBinding().getVariable("i"));

script= null;

shell= null;

}

二. GroovyClassLoader代码示例

1) 解析groovy文件

/*** from source file of *.groovy*/

public static void parse() throwsException{

GroovyClassLoader classLoader= newGroovyClassLoader(Thread.currentThread().getContextClassLoader());

File sourceFile= new File("D:\\TestGroovy.groovy");

Class testGroovyClass= classLoader.parseClass(newGroovyCodeSource(sourceFile));

GroovyObject instance= (GroovyObject)testGroovyClass.newInstance();//proxy

Long time = (Long)instance.invokeMethod("getTime", newDate());

System.out.println(time);

Date date= (Date)instance.invokeMethod("getDate", time);

System.out.println(date.getTime());//here

instance = null;

testGroovyClass= null;

}

2) 如何加载已经编译的groovy文件(.class)

public static void load() throwsException {

GroovyClassLoader classLoader= newGroovyClassLoader(Thread.currentThread().getContextClassLoader());

BufferedInputStream bis= new BufferedInputStream(new FileInputStream("D:\\TestGroovy.class"));

ByteArrayOutputStream bos= newByteArrayOutputStream();for(;;){int i =bis.read();if( i == -1){break;

}

bos.write(i);

}

Class testGroovyClass= classLoader.defineClass(null, bos.toByteArray());//instance of proxy-class//if interface API is in the classpath,you can do such as://MyObject instance = (MyObject)testGroovyClass.newInstance()

GroovyObject instance =(GroovyObject)testGroovyClass.newInstance();

Long time= (Long)instance.invokeMethod("getTime", newDate());

System.out.println(time);

Date date= (Date)instance.invokeMethod("getDate", time);

System.out.println(date.getTime());//here

bis.close();

bos.close();

instance= null;

testGroovyClass= null;

}

三. ScriptEngine

1) pom.xml依赖

org.codehaus.groovy

groovy

2.1.6

org.codehaus.groovy

groovy-jsr223

2.1.6

2) 代码样例

public static void evalScript() throwsException{

ScriptEngineManager factory= newScriptEngineManager();//每次生成一个engine实例

ScriptEngine engine = factory.getEngineByName("groovy");

System.out.println(engine.toString());assert engine != null;//javax.script.Bindings

Bindings binding =engine.createBindings();

binding.put("date", newDate());//如果script文本来自文件,请首先获取文件内容

engine.eval("def getTime(){return date.getTime();}",binding);

engine.eval("def sayHello(name,age){return 'Hello,I am ' + name + ',age' + age;}");

Long time= (Long)((Invocable)engine).invokeFunction("getTime", null);

System.out.println(time);

String message= (String)((Invocable)engine).invokeFunction("sayHello", "zhangsan",new Integer(12));

System.out.println(message);

}

需要提醒的是,在groovy中,${expression} 将会被认为一个变量,如果需要输出"$"符号,需要转义为"\$".

关于ScriptEngine更多介绍,请参考.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值