Java使用Aviator表达式 学习记录(二十五)

1,454 阅读3分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

调用脚本函数

在 java 中调用 script 函数也同样支持:

package com.googlecode.aviator.example.scripting;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class InvokeScriptFunction {
  public static void main(final String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AviatorScript");

    // AviatorScript code in a String
    String script = "fn hello(name) { print('Hello, ' + name); }";
    // evaluate script
    engine.eval(script);

    // javax.script.Invocable is an optional interface.
    // Check whether your script engine implements or not!
    // Note that the AviatorScript engine implements Invocable interface.
    Invocable inv = (Invocable) engine;

    // invoke the global function named "hello"
    inv.invokeFunction("hello", "Scripting!!" );
  }
}

我们在脚本里定义了 hello 函数,然后通过 Invocable 接口就可以在 java 代码里调用并传入参数:

Hello, Scripting!!

在 AviatorScript 中可以使用 map 和闭包来模拟面向对象编程,同样,我们可以在 java 代码里调用 AviatorScript 中“对象”的方法:

package com.googlecode.aviator.example.scripting;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class InvokeScriptMethod {
  public static void main(final String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AviatorScript");

    // AviatorScript code in a String. This code defines a script object 'obj'
    // with one method called 'hello'.
    String script =
        "let obj = seq.map(); obj.hello = lambda(name) -> print('Hello, ' + name); end;";
    // evaluate script
    engine.eval(script);

    // javax.script.Invocable is an optional interface.
    // Check whether your script engine implements or not!
    // Note that the AviatorScript engine implements Invocable interface.
    Invocable inv = (Invocable) engine;

    // get script object on which we want to call the method
    Object obj = engine.get("obj");

    // invoke the method named "hello" on the script object "obj"
    inv.invokeMethod(obj, "hello", "Script Method !!");
  }
}

我们定义了对象 obj ,它有一个方法 hello(name) ,在 java 代码里通过 engine.get("obj") 获取该对象,然后通过 Invocable 接口调用  invokeMethod(obj, 方法名,方法参数列表) 就可以调用到该对象的方法。

使用脚本实现 Java 接口

我们可以用 AviatorScript 脚本实现 java 中的接口,然后将函数或者对象方法转成该接口在 java 代码里使用,比如我们用 AviatorScript 实现 Runnable 接口:

package com.googlecode.aviator.example.scripting;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class RunnableImpl {
  public static void main(final String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AviatorScript");

    // AviatorScript code in a String
    String script = "fn run() { println('run called'); }";

    // evaluate script
    engine.eval(script);

    Invocable inv = (Invocable) engine;

    // get Runnable interface object from engine. This interface methods
    // are implemented by script functions with the matching name.
    Runnable r = inv.getInterface(Runnable.class);

    // start a new thread that runs the script implemented
    // runnable interface
    Thread th = new Thread(r);
    th.start();
  }
}

我们在  AviatorScript 实现了一个 run() 函数, 接下来就可以从引擎里获取一个 Runnable 接口实现,它会自动去调用已定义的 run 函数并执行,然后我们将获取的 Runnable 实例用在了线程里:

run called

不仅是函数,对于“对象”也同样可以的:

package com.googlecode.aviator.example.scripting;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class RunnableImplObject {
  public static void main(final String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AviatorScript");

    // AviatorScript code in a String
    String script =
        "let obj = seq.map(); obj.run = lambda() -> println('run method called'); end; ";

    // evaluate script
    engine.eval(script);

    // get script object on which we want to implement the interface with
    Object obj = engine.get("obj");

    Invocable inv = (Invocable) engine;

    // get Runnable interface object from engine. This interface methods
    // are implemented by script methods of object 'obj'
    Runnable r = inv.getInterface(obj, Runnable.class);

    // start a new thread that runs the script implemented
    // runnable interface
    Thread th = new Thread(r);
    th.start();
  }
}

你可以将某个对象的方法转成特定接口的实现。

多 Scope 支持

在上面的注入变量一节,我们注入了全局变量,Scripting API 也支持多个全局变量环境同时执行,相互隔离,这是通过 ScriptContext 实现,你可以把他理解成一个类似 Map<String, Object> 的映射:

package com.googlecode.aviator.example.scripting;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;

public class MultiScopes {
  public static void main(final String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("AviatorScript");

    engine.put("x", "hello");
    // print global variable "x"
    engine.eval("println(x);");
    // the above line prints "hello"

    // Now, pass a different script context
    ScriptContext newContext = new SimpleScriptContext();
    Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);

    // add new variable "x" to the new engineScope
    engineScope.put("x", "world");

    // execute the same script - but this time pass a different script context
    engine.eval("println(x);", newContext);
    // the above line prints "world"
  }
}

newContext 的 engine 级别绑定里我们重新定义了 x 为 world 字符串,并传入 eval 执行,两者打印的结果将不同:

hello
world