JAVA嵌入運行Groovy腳本

最近設計一個數據統計系統,系統中上百種數據統計維度,並且這些數據統計的指標可能隨時會調整.若是基於java編碼的方式逐個實現數據統計的API設計,工做量大並且維護起來成本較高;最終肯定爲將"數據統計"的計算部分單獨分離成腳本文件(javascript,或者Groovy),很是便捷了實現了"數據統計Task" 與 "數據統計規則(計算)"解耦,且能夠動態的加載和運行的能力.順便對JAVA嵌入運行Groovy腳本作個備忘.javascript

    Java中運行Groovy,有三種比較經常使用的類支持:GroovyShell,GroovyClassLoader以及Java-Script引擎(JSR-223).java

    1) GroovyShell: 一般用來運行"script片斷"或者一些零散的表達式(Expression)shell

    2) GroovyClassLoader: 若是腳本是一個完整的文件,特別是有API類型的時候,好比有相似於JAVA的接口,面向對象設計時,一般使用GroovyClassLoader.express

    3) ScriptEngine: JSR-223應該是推薦的一種使用策略.規範化,並且簡便.session

 

一.GroovyShell代碼樣例app

    1) 簡單的表達式執行,方法調用this

 /** 
 * 簡答腳本執行 
 * @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;  
}

2)  僞main方法執行.編碼

 /** 
 * 當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腳本lua

 /** 
 * 運行完整腳本 
 * @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)  方法執行和分部調用spa

     /** 
     * 以面向"過程"的方式運行腳本 
     * @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文件

     /** 
     * 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)

     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依賴

 <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) 代碼樣例

 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} 將會被認爲一個變量,若是須要輸出"$"符號,須要轉義爲"\$".   

關於ScriptEngine更多介紹,請參考.

相關文章
相關標籤/搜索