欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java平台使用脚本语言

程序员文章站 2024-03-17 23:32:40
...

Java平台使用脚本语言

​ java平台可以使用的脚本语言

  1. js
  2. groovy
  3. Renjin
  4. sisc

使用java提供的脚本引擎对脚本语言进行解析

​ java提供了一个脚本引擎:ScriptEngine,我们需要通过这个脚本引擎来对我们的脚本语言进行解析。

获取ScriptEngine

​ 构造一个ScriptEngineManager,并调用getEngineByxx方法来获取对应的ScriptEngine

//构建一个脚本引擎管理器
ScriptEngineManager manager = new ScriptEngineManager();

//获取js的解析引擎
ScriptEngine jsEngine = manager.getEngineByName("js");

获取当前java所支持的引擎名,MIME类型和文件扩展名

​ 可使用ScriptEngineManager 的getEngineFactories()方法来获取。

ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();

​ jdk1.8,当前内置只支持一个脚本引擎:Nashorn;

​ 这是有Oracle开发的一个js解释器,如果想用使用其他脚本语言的解释器,可以通过在类路径中提供必要的jar包来添加对更多语言的支持。----《java核心技术卷二》 p353

使用js脚本引擎解析js脚本

简单运行

​ 以下代码仅仅对于js脚本解析器的简单使用,通过jsEngine.eval();方法传入一段脚本,或者一个js文件输入流;脚本的内容也很简单就是将var a = 10;然后通过jsEngine.get(“key”),方法从脚本中读取一个var变量进行输出;但这远远不是js脚本引擎的所有功能。

public static void main(String[] args) throws Exception {

    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine jsEngine = manager.getEngineByName("js");

    //直接使用string 模拟脚本语言进行测试
    String jsScript = "var a = 10";
    jsEngine.eval(jsScript);
    int strA = (int)jsEngine.get("a");
    System.out.println(strA);
    //使用js脚本文件进行测试
    File file = new File("test.js");

    if (!file.exists()) {
        return;
    }

    Reader reader = new FileReader(file);

    jsEngine.eval(reader);
    int fileA = (int)jsEngine.get("a");
    System.out.println(fileA);
}

ScriptEngine中的方法

  • eval(“script”):执行传入的脚本,如果该脚本有返回值,就返回该值;

  • get(“key”):从脚本获取key对应的value

  • put"(“key”,value):在引擎作用域内传入一个key = value的键值对

  • createBindings():创建一个适合该引擎的空Bindings对象

  • getBindings():返回绑定的Bindings对象

  • setBindings():

  • getContext()

  • setContext()

  • getFactory()

引擎作用域和全局作用域

​ 引擎作用域:由ScriptEngine使用eval或者put方法,在脚本中构建的key:value键值对;

​ 全局作用域:有ScriptEngineManage使用put方法,在脚本中够构建的key:value键值对;

​ 其他作用域:实现ScriptContext接口的类,这些类可以作为自定义的作用域,由一个整数标识,越小的数字应该越先被搜索到。

Bindings对象

​ key:value:可以称为一个变量绑定;而Bindings对象可以存放多个key:value这样的绑定,而且可以在同一个作用域中的其他绑定的变量;

public static void main(String[] args) throws ScriptException {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine jsEngine = manager.getEngineByName("js");
    Bindings jsBindings = jsEngine.createBindings();
    
    jsBindings.put("a",10);
    jsEngine.eval("var b = a",jsBindings);

    Object b = jsBindings.get("b");
    System.out.println(b);

    Object b2 = jsBindings.get("b");//10,说明 一个变量绑定是可以多次使用的
    System.out.println(b2);

    Object b4 = jsEngine.get("b");
    System.out.println(b4);//null, 说明在使用eval方法时传入一个Binding对象,传入脚本的变量绑定也会放在这个Binding对象中

    jsEngine.eval("var e = 30");
    Object e = jsEngine.get("e");
    System.out.println("e" + e);//30,说明如果没有传入Binding对象,则会放到jsEngine的公共域中

    Object b1 = manager.get("b");
    System.out.println(b1);//null 说明 b 变量 是属于 jsEngine的

    manager.put("c",20);
    Object c = jsEngine.get("c");
    System.out.println(c);// 20 说明 c 变量 是属于公公域的
}

重定向输入和输出

​ 可以通过脚本上下文(ScriptEngine)的setReader和SetWriter方法来重定向脚本的标准输入和输出

​ 如下数据,world就会被重定向到控制台输出

print("Hello");
java.lang.System.out.println("World");

​ 代码如下

public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine jsEngine = manager.getEngineByName("js");

    StringWriter writer = new StringWriter();//设置一个输出流
    jsEngine.getContext().setWriter(new PrintWriter(writer,true));//修改数据输出方式
    Reader reader = new FileReader(new File("redirct.js"));
    jsEngine.eval(reader);

}

​ 由于Nashorn引擎没有标准的输入源的概念,因此调用setReader没有效果。

​ 除了可以设置脚本正常运行的输出,也可以设置脚本运行出错的输出:

​ setErrorWriter(Writer writer);

调用脚本的函数和方法

​ 使用ScriptEngine调用javaScript或者其他脚本语言的方式有以下几种。

  • 使用脚本中的函数的函数名来调用: InvokeFunction()
  • 如果脚本语言是面向对象的,就可以使用: InvokeMethod()
  • 让脚本引擎去实现一个接口,通过这个接口的实例对象去调用脚本中的方法

InvokeFunction

​ 使用该方法调用脚本的函数需要注意的是,对应的脚本引擎必须实现了Invokeable接口,即可以使用java的代理机制。具体内容在《java核心技术卷一第6章》p258

​ 首先要在脚本文件中或者再脚本字符串中有一个方法;有无返回值并没有关系;例如我这边使用的是外置的js文件。

function greet(how, whom) {
    return how + ',' + whom + '!';
}

​ 然后使用ScriptEngine的eval方法将这个脚本文件传到这个引擎作用域中。

Reader reader = new FileReader(new File("jsFunction.js"));
jsEngine.eval(reader);//将脚本传入js解析器
//调用该脚本中的方法
Object result = ((Invocable) jsEngine).invokeFunction("greet", "hello", "world");
System.out.println(result);
//输出:hello,world!

​ 小结:使用invokeFunction使用java代码区调用js函数,首先需要将当前的ScriptEngine强转为Invocable接口类型,然后需要知道这个js函数的函数名,并且需要传入这个js函数需要的参数。这样就可以使用这个脚本中的函数了。

InvokeMethod

​ 如果脚本语言是面向对象的脚本语言,例如js;则可以使用InvokeMethod方法去调用当前脚本的函数。

function Greet(how) {
    this.how = how;
}

Greet.prototype.welcome= function (whom) {
    return this.how + ',' + whom + '!';
}

​ 上面的代码其实就是在js中申明了一个方法Greet,并且用这个方法的prototype对象又添加了一个方法welcome;

//将上述js文件传入当前引擎作用域
sEngine.eval(reader);
//实例化一个Greet方法的对象
Object yo = jsEngine.eval("new Greet('Yo')");
//传入当前Greet方法的对象,以及其内部方法welcome的方法名和参数
Object result = ((Invocable) jsEngine).invokeMethod(yo,"welcome","World");
//输出返回值
System.out.println(result);
//Yo,World

​ 小结:必须为面向对象的脚本语言;

让脚本引擎去实现一个java接口,使用这个接口的java方法去调用脚本中的函数。

​ 脚本中的方法名和参数必须和java接口中的方法名和参数一致。

​ js文件

function welcome(whom) {
    return 'hello' + whom + '!'
}

​ java代码:

​ 定义的接口

public interface Greeter {
    String welcome(String whom);
}

​ 实现

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine js = manager.getEngineByName("js");
js.eval(new FileReader(new File("jsFunctionInterface")));

//让当前脚本引擎实现Greeter接口,并返回这个接口的一个实例
Greeter g = ((Invocable) js).getInterface(Greeter.class);
//使用这个接口中的welcom方法去调用脚本中的函数
String world = g.welcome("world");
System.out.println(world);
//helloworld!

​ 在面向对象的脚本语言中,可以通过相匹配的java接口来访问一个脚本类;

//这里用到js代码是InvokeMethod中的js代码
jsEngine.eval(reader);
Object yo = jsEngine.eval("new Greet('Yo')");
Greeter g = ((Invocable) jsEngine).getInterface(yo, Greeter.class);
String world = g.welcome("World");
System.out.println(world);

编译脚本,提高脚本在jav中的执行效率

​ 如果当前的脚本引擎实现了Compilable接口,就可以将这些脚本代码编译为某种中间格式,提高执行效率。

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine jsEngine = manager.getEngineByName("js");
Reader reader = new FileReader(new File("test.js"));
CompiledScript script = null;
if (jsEngine instanceof Compilable) {
    script = ((Compilable)jsEngine).compile(reader);
}

if (script != null) {
    Object eval = script.eval();
    System.out.println(eval);
} else {
    Object eval = jsEngine.eval(reader);
    System.out.println("uncompilable" + eval);
}

​ 当前不是很清楚将脚本编译后该怎么调用脚本中的函数。。。。