最近设计一个数据统计系统,系统中上百种数据统计维度,而且这些数据统计的指标可能随时会调整.如果基于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应该是推荐的一种使用策略.规范化,而且简便.

官方参考文档: http://docs.groovy-lang.org/latest/html/documentation/guide-integrating.html

一.GroovyShell代码样例

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

Java代码

* 简答脚本执行   * @throws Exception  public static void evalScriptText() throws Exception{       //groovy.lang.Binding       Binding binding = new Binding();       GroovyShell shell = new GroovyShell(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;

GroovyShell是一种性能较低的方式,因为每次都需要创建shell和script,这也意味着每次都需要对expression进行“编译”(JAVA Class)。

2)  伪main方法执行.

Java代码

* 当groovy脚本,为完整类结构时,可以通过执行main方法并传递参数的方式,启动脚本.  public static void evalScriptAsMainMethod(){       String[] args = new String[]{"Zhangsan","10"};//main(String[] args)       Binding binding = new Binding(args);       GroovyShell shell = new GroovyShell(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脚本

Java代码

* 运行完整脚本   * @throws Exception  public static void evalScriptTextFull() throws Exception{       StringBuffer buffer = new StringBuffer();       //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 = new Binding();       GroovyShell shell = new GroovyShell(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)  方法执行和分部调用

Java代码

* 以面向"过程"的方式运行脚本   * @throws Exception  public static void evalScript() throws Exception{       Binding binding = new Binding();       GroovyShell shell = new GroovyShell(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文件

Java代码

* from source file of *.groovy  public static void parse() throws Exception{       GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());       File sourceFile = new File("D:\\TestGroovy.groovy");//文本内容的源代码       Class testGroovyClass = classLoader.parseClass(new GroovyCodeSource(sourceFile));       GroovyObject instance = (GroovyObject)testGroovyClass.newInstance();//proxy       Long time = (Long)instance.invokeMethod("getTime", new Date());       System.out.println(time);       Date date = (Date)instance.invokeMethod("getDate", time);       System.out.println(date.getTime());       //here       instance = null;       testGroovyClass = null;

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

Java代码

public static void load() throws Exception {  
    GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());  
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\TestGroovy.class"));  
    ByteArrayOutputStream bos = new ByteArrayOutputStream();  
    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", new Date());  
    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依赖

Xml代码 

<dependency>  
    <groupId>org.codehaus.groovy</groupId>  
    <artifactId>groovy</artifactId>  
    <version>2.1.6</version>  
</dependency>  
<dependency>  
    <groupId>org.codehaus.groovy</groupId>  
    <artifactId>groovy-jsr223</artifactId>  
    <version>2.1.6</version>  
</dependency>  

    2) 代码样例

Java代码 

public static void evalScript() throws Exception{  
    ScriptEngineManager factory = new ScriptEngineManager();  
    //每次生成一个engine实例  
    ScriptEngine engine = factory.getEngineByName("groovy");  
    System.out.println(engine.toString());  
    assert engine != null;  
    //javax.script.Bindings  
    Bindings binding = engine.createBindings();  
    binding.put("date", new Date());  
    //如果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} 将会被认为一个变量,如果需要输出"$"符号,需要转义为"\$".   

    这是一种性能较高的方式,engine我们可以声明为全局实例,是线程安全的。每次调用时只需要创建新的Binndings即可,此外如果脚本已经编译过(首次执行之后)其Class将会被缓存,则此后不需要再次编译。

运行javascript脚本

    try {
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
            Compilable compilable = (Compilable) engine;
            Bindings bindings = engine.createBindings(); //Local级别的Binding
            //定义函数并调用
            String script = "function add(op1,op2){return op1+op2} add(a, b)";
            //解析编译脚本函数
            CompiledScript JSFunction = compilable.compile(script);
            bindings.put("a", 1);bindings.put("b", 2); //通过Bindings加入参数
            Object result = JSFunction.eval(bindings);
            System.out.println(result); //调用缓存着的脚本函数对象,Bindings作为参数容器传入
        }catch (ScriptException e) {
            e.printStackTrace();

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

最近设计一个数据统计系统,系统中上百种数据统计维度,而且这些数据统计的指标可能随时会调整.如果基于java编码的方式逐个实现数据统计的API设计,工作量大而且维护起来成本较高;最终确定为将"数据统计"的计算部分单独分离成脚本文件(javascript,或者Groovy),非常便捷了实现了"数据统计Task" 与 "数据统计规则(计算)"解耦,且可以动态的加载和运行的能力.顺便对JAVA嵌入运行Groovy脚本做个备忘. Java中运行Groovy,有三种比较常用的类支持:GroovyShell,G..
javagroovy、C#、Javascript的运行效率到底如何? 通过实验比较,Javascript的运行效率解决java,看来基于javascript的html5前途远大。 C#的Release版的运行性能也相当高,和C++相差不大,值得推荐。 文件内部包含测试代码,供参考
经过初步了解发现groovy这门语言,竟然和java无缝对接,真的快速上手。学习了几门语言发现:一切高阶的语言都是建立在底层语言的基础上的,比如kotlin在java的基础上进行了扩展封装。本文的groovy,也是对java的扩展封装。java的底层也c++实现的,因此有号称c+++的称号。有了几门语言基础学习其他的就快多了。。。 一、基本语法 (1)变量类型 Groovy是一种基于Java平台的面向对象语言。 在Groovy,如在任何其他面向对象语言一样,存在类和对象的概念以表示编程语言的对象定向性质。 JavaGroovy的语法非常相似。 Groovy 可以与 Java 语言无缝对接,在写 Groovy 的时候如果忘记了语法可以直接按Java的语法继续写,也可以在 Java 调用 Groovy 脚本,都可以很好的工作,这有效的降低...
scott@refactr.com 原文出自GroovyMag sampler,也就是免费下载的那一期。有兴趣的朋友可以去http://www.groovymag.com/注册并免费下载。 作者自称在开发RIA过程使用了大量javascript。虽然可以用selenium+jsUnit进... Mocking JavaScript 对象行为 Javascript的语言特点决定了其对象的行为可以在对象被定义好后被轻易改变。比如下面代码里这个简单的例子,Conference对象的getTheName方法被定义为一个返回字符串的function Conference.prototyp... – 使得 Java 应用程序可以通过一套固定的接口与各种脚本引擎交互,从 而达到在 Java 平台上调用各种脚本语言的目的。 – Java 脚本 API 是连通 Java 平台和脚本语言的桥梁。 – 可以把一些复杂异变的业务逻辑交给脚本语言处理,这又大大提高了 开发效率。 • 获得脚本引擎对象 //获得脚本引擎对象 ScriptEngineManager sem = new ScriptEngineManager(); ScriptEn
您可以使用以下方法在Java项目集成Groovy: 1. 在项目添加Groovy依赖。 例如,使用Gradle您可以在build.gradle文件添加以下行: dependencies { compile 'org.codehaus.groovy:groovy:2.4.12' 2. 使用Groovy编写脚本或类,并在Java代码使用它们。 例如,下面是一个简单的Groovy脚本: def greet(name) { println "Hello, $name!" greet('Alice') 您可以在Java代码使用GroovyShell类来执行这个脚本: import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; import groovy.lang.Binding; import groovy.lang.GroovyShell; CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer imports = new ImportCustomizer(); imports.addImports("org.example.Foo"); config.addCompilationCustomizers(imports); Binding binding = new Binding(); binding.setVariable("name", "Alice"); GroovyShell shell = new GroovyShell(binding, config); shell.evaluate("greet(name)"); 还有许多其他方法可以在Java项目使用Groovy,这取决于您的具体需求。 如果您需要更多帮助,请告诉我您的具体需求,我会尽力为您提供帮助。