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

1,498 阅读4分钟

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

Java Scripting API 支持

6.3 节我们看到了如何在 AviatorScript 中调用 Java 函数,这里我们将介绍如何用 Java 提供的脚本 API 来调用 AviatorScript 脚本中的函数等。

AviatorScript 内置了对 ****Java Scripting API 的支持,并且提供了 AviatorScriptEngineFactory 的 SPI 实现,只要你的 classpath 包含了 aviator 的 jar 引用,就可以直接使用。我们来看一些例子(所有示例代码在源码 example 的 scripting 目录)。

获取执行引擎

通过 ScriptEngineManager 可以获得 AviatorScript 的执行引擎:

package com.googlecode.aviator.example.scripting;

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

public class ScriptEngineExample {
  public static void main(final String[] args) {
    final ScriptEngineManager sem = new ScriptEngineManager();
    ScriptEngine engine = sem.getEngineByName("AviatorScript");

  }
}

接下来我们将使用这个 engine 做各种例子演示。

配置执行引擎

可以从 ScriptEngine 里获取底层的 AviatorEvaluatorInstance 引用,进行引擎的相关配置:

package com.googlecode.aviator.example.scripting;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Feature;
import com.googlecode.aviator.Options;
import com.googlecode.aviator.script.AviatorScriptEngine;

public class ConfigureEngine {
  public static void main(final String[] args) throws Exception {
    final ScriptEngineManager sem = new ScriptEngineManager();
    ScriptEngine engine = sem.getEngineByName("AviatorScript");
    AviatorEvaluatorInstance instance = ((AviatorScriptEngine) engine).getEngine();
    // Use compatible feature set
    instance.setOption(Options.FEATURE_SET, Feature.getCompatibleFeatures());
    // Doesn't support if in compatible feature set mode.
    engine.eval("if(true) { println('support if'); }");
  }
}

默认的引擎处于下列模式:

  1. 全语法特性支持
  2. 缓存编译模式
  1. 启用基于反射的 java 方法调用

求值

最简单的,你可以直接执行一段 AviatorScript 脚本,调用 eval(script) 方法即可

package com.googlecode.aviator.example.scripting;

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

public class EvalScriptExample {
  public static void main(final String[] args) throws Exception {
    final ScriptEngineManager sem = new ScriptEngineManager();
    ScriptEngine engine = sem.getEngineByName("AviatorScript");
    engine.eval("print('Hello, World')");
  }
}

这将打印 Hello, World 到控制台,调用了 print 函数,

如果你的脚本是文件,也可以用 eval(reader) 方法:

import javax.script.*;
public class EvalFile {
    public static void main(String[] args) throws Exception {
        // create a script engine manager
        ScriptEngineManager factory = new ScriptEngineManager();
        // create AviatorScript engine
        ScriptEngine engine = factory.getEngineByName("AviatorScript");
        // evaluate AviatorScript code from given file - specified by first argument
        engine.eval(new java.io.FileReader(args[0]));
    }
}

文件名通过执行的第一个参数指定。

默认引擎处于缓存表达式模式。

注入变量

可以注入全局变量到脚本,并执行:

package com.googlecode.aviator.example.scripting;

import java.io.File;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

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

    File f = new File("test.txt");
    // expose File object as variable to script
    engine.put("file", f);

    // evaluate a script string. The script accesses "file"
    // variable and calls method on it
    engine.eval("print(getAbsolutePath(file))");
  }

}

这里我们将文件 f 通过 engine.put 方法作为全局变量注入,然后执行脚本 print(getAbsolutePath(file)) ,打印文件的绝对路径。

默认引擎启用了基于 java 反射的方法调用模式。

编译脚本并执行

AviatorScript 也支持了 Scripting API 的预编译模式:

package com.googlecode.aviator.example.scripting;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

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

    Compilable compilable = (Compilable) engine;
    CompiledScript script = compilable.compile("a + b");

    final Bindings bindings = engine.createBindings();
    bindings.put("a", 99);
    bindings.put("b", 1);
    System.out.println(script.eval(bindings));
  }

}

我们将表达式 a+b 编译成一个 CompiledScript 对象,接下来通过 createBindings 创建了一个环境绑定,将 a 和 b 分别绑定为 99 和 1,然后执行 eval(bindings) ,结果为 100。

默认编译也是启用缓存表达式模式。

调用脚本函数

在 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, 方法名,方法参数列表) 就可以调用到该对象的方法。