Java中運行動態腳本


這里主要總結Java中集成Groovy的應用。

Groovy可以與Java完美集成來擴展我們的應用,比如替代Java+jexl實現算式表達式計算或其它功能。在Ofbiz中也集成了Groovy來執行一些查詢功能,並且是開始更多的使用Groovy而不是原有的bsh。這里僅僅初步總結我們在Java項目中如何來應用Groovy擴展我們的應用。
1.使用GroovyShell計算表達式
使用Binding對象將變量傳入表達式,並通過GroovyShell返回表達式的計算結果。如下例:
public class GroovyShellExample {
    public static void main(String args[]) {
        Binding binding = new Binding();
        binding.setVariable("x", 10);
        binding.setVariable("language", "Groovy");

        GroovyShell shell = new GroovyShell(binding);
        Object value = shell.evaluate("println \"Welcome to $language\"; y = x * 2; z = x * 3; return x ");

        System.err.println(value +", " + value.equals(10));
        System.err.println(binding.getVariable("y") +", " + binding.getVariable("y").equals(20));
        System.err.println(binding.getVariable("z") +", " + binding.getVariable("z").equals(30));
    }
}
運行結果如下:
Welcome to Groovy
10, true
20, true
30, true

2.使用GroovyScriptEngine腳本引擎加載Groovy腳本
GroovyScriptEngine從指定的位置(文件系統,URL,數據庫等等)加載Groovy腳本,並且隨着腳本變化可重新加載它們。和GroovyShell一樣,GroovyScriptEngine也可以傳進變量值返回腳本的計算結果。這樣我們可以把一些可用的計算公式或計算條件寫入Groovy腳本中來執行應用計算。當這些公式或計算條件變更時,我們可更方便地進行更改計算。如:
public class GroovyScriptEngineExample {
    public static void main(String args[]) {
        try {
            String[] roots = new  String[]{".\\src\\sample\\"} ;//定義Groovy腳本引擎的根路徑
            GroovyScriptEngine engine = new GroovyScriptEngine(roots);
            Binding binding = new Binding();
            binding.setVariable("language", "Groovy");
            Object value = engine.run("SimpleScript.groovy", binding);
            assert value.equals("The End");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
SimpleScript.groovy腳本如下:
//SimpleScript.groovy
println "Welcome to $language"
return "The End"

運行結果如下:
Welcome to Groovy

3.使用GroovyClassLoader動態地載入Groovy的類
下例現示如何使用GroovyClassLoader加載Groovy類並且調用該類的一個方法
package sample;
public class GroovyClassLoaderExample {
    public static void main(String args[]) {
        try {
            GroovyClassLoader loader = new GroovyClassLoader();
            Class fileCreator = loader.parseClass(new File("GroovySimpleFileCreator.groovy"));
            GroovyObject object = (GroovyObject) fileCreator.newInstance();
            object.invokeMethod("createFile", "C:\\temp\\emptyFile.txt");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
GroovySimpleFileCreator.groovy文件如下:
package sample;
class GroovySimpleFileCreator {
    public createFile(String fileName){
        File file = new File(fileName);
        file.createNewFile();
    }
}
使用GroovyClassLoader另一種情景便是:存在一個Java接口和一個實現該Java接口的Groovy類。此時,可以通過GroovyClassLoader加載Groovy實現類到應用中,這樣就可以直接調用該接口的方法。
接口定義如下:
package sample;
public interface IFoo {
    Object run(Object foo);
}
package sample;
public class InvokeGroovy {
    public static void main(String[] args) {
        ClassLoader cl = new InvokeGroovy().getClass().getClassLoader();
        GroovyClassLoader groovyCl = new GroovyClassLoader(cl);
        try {
            //從文件中讀取,將實現IFoo接口的groovy類寫在一個groovy文件中
            //Class groovyClass = groovyCl.parseClass(new File("./src/sample/Foo.groovy"));
            //直接使用Groovy字符串,也可以獲得正確結果
            Class groovyClass = groovyCl.parseClass("package sample; \r\n class Foo implements IFoo {public Object run(Object foo) {return 2+2>1}}");//這個返回true
            IFoo foo = (IFoo) groovyClass.newInstance();
            System.out.println(foo.run(new Integer(2)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.使用JAVA腳本API
Java SE 6 引入了對 Java Specification Request(JSR)223 的支持,JSR 223 旨在定義一個統一的規范,使得 Java 應用程序可以通過一套固定的接口與各種腳本引擎交互,從而達到在 Java 平台上調用各種腳本語言的目的。每一個腳本引擎就是一個腳本解釋器,負責運行腳本,獲取運行結果。ScriptEngine 接口提供了許多 eval 函數的變體用來運行腳本,這個函數的功能就是獲取腳本輸入,運行腳本,最后返回輸出。
下例顯示了一個使用JAVA腳本API運行Groovy的例子:
public class GroovyJSR223Example {
    public static void main(String args[]) {
        try {
            ScriptEngineManager factory = new ScriptEngineManager();
            ScriptEngine engine = factory.getEngineByName("groovy");
            String HelloLanguage = "def hello(language) {return \"Hello $language\"}";
            engine.eval(HelloLanguage);
            Invocable inv = (Invocable) engine;
            Object[] params = {new String("Groovy")};
            Object result = inv.invokeFunction("hello", params);
            assert result.equals("Hello Groovy");
            System.err.println(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Java腳本API除了可以運行Groovy外,還可以運行其它腳本程序,如JavaScript、BSH等。

以下參考<<Java SE 6 新特性: 對腳本語言的支持>> http://www.ibm.com/developerworks/cn/java/j-lo-jse66/
javax.script.ScriptContext 接口和 javax.script.Bindings 接口定義了腳本引擎的上下文
   ? Bindings 接口:Java 應用程序和腳本程序通過這些“鍵-值”對交換數據。
   ? ScriptContext 接口:ScriptEngine 通過 ScriptContext 實例就能從其內部的 Bindings 中獲得需要的屬性值。ScriptContext 接口默認包含了兩個級別的 Bindings 實例的引用,分別是全局級別和引擎級別.ScriptContext 還允許用戶重定向引擎執行時的輸入輸出流。
public class Redirectory {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");

        PipedReader pr = new PipedReader();
        PipedWriter pw = new PipedWriter(pr);
        PrintWriter writer = new PrintWriter(pw);
        engine.getContext().setWriter(writer);

        String script = "println('Hello from JavaScript')";
        engine.eval(script);
       
        BufferedReader br =new BufferedReader(pr);
        System.out.println(br.readLine());
    }
}
共有三個級別的地方可以存取屬性,分別是 ScriptEngineManager 中的 Bindings,ScriptEngine 實例對應的 ScriptContext 中含有的 Bindings,以及調用 eval 函數時傳入的 Bingdings。離函數調用越近,其作用域越小,優先級越高。下例中可以看出各個屬性的存取優先級:
public class ScopeTest {
    public static void main(String[] args) throws Exception {
        String script="println(greeting)";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
       
        //Attribute from ScriptEngineManager
        manager.put("greeting", "Hello from ScriptEngineManager");
        engine.eval(script);

        //Attribute from ScriptEngine
        engine.put("greeting", "Hello from ScriptEngine");
        engine.eval(script);

        //Attribute from eval method
        ScriptContext context = new SimpleScriptContext();
        context.setAttribute("greeting", "Hello from eval method", ScriptContext.ENGINE_SCOPE);
        engine.eval(script,context);
    }
}

在 Java 腳本 API 中還有兩個腳本引擎可以選擇是否實現的接口,這個兩個接口不是強制要求實現的,即並非所有的腳本引擎都能支持這兩個函數:
   ? Invocable 接口:允許 Java 平台調用腳本程序中的函數或方法。Invocable 接口還允許 Java 應用程序從這些函數中直接返回一個接口,通過這個接口實例來調用腳本中的函數或方法,從而我們可以從腳本中動態的生成 Java 應用中需要的接口對象。
   ? Compilable 接口:允許 Java 平台編譯腳本程序,供多次調用。
下例調用腳本中的函數:
public class CompilableTest {
    public static void main(String[] args) throws ScriptException, NoSuchMethodException {
        String script = " function greeting(message){println (message);}";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
        engine.eval(script);

        if (engine instanceof Invocable) {
            Invocable invocable = (Invocable) engine;
            invocable.invokeFunction("greeting", "hi");
            // It may through NoSuchMethodException
            try {
                invocable.invokeFunction("nogreeing");
            } catch (NoSuchMethodException e) {
                // expected
            }
        }
    }
}
下例演示了如何使用 Compiable 接口來調用腳本:
public class CompilableTest {
    public static void main(String[] args) throws ScriptException {
        String script = " println (greeting); greeting= 'Good Afternoon!' ";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
        engine.put("greeting", "Good Morning!");
       
        if (engine instanceof Compilable) {
            Compilable compilable = (Compilable) engine;
            CompiledScript compiledScript = compilable.compile(script);
            compiledScript.eval();
            compiledScript.eval();
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM