Rhino:如何将Java对象传递给脚本,其中可以引用为“this”

Rhino: How to pass Java object to script, where can be referenced as "this"

本文关键字:引用 this 脚本 Java 对象 Rhino      更新时间:2023-09-26

我是JSR-223 Java脚本的新手,实际上我是从MVEL切换到标准的Mozilla Rhino JS。我已经阅读了所有的文档,但还是卡住了。我试图引用一些Java对象从脚本绑定就像在教程:

    // my object
    public class MyBean {
       public String getStringValue() { return "abc" };
    }
    // initialization
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    // add bindings
    engine.put("bean", new MyBean());
    // evaluate script, output is "abc"
    engine.eval("print(bean.stringValue)");

Java对象作为属性bean从脚本引用。到目前为止一切顺利。

但是我想在脚本中引用我的对象作为this,我想使用它的属性和方法没有任何前缀或显式地使用前缀this。就像这样:

    // add bindings
    engine.put(....., new MyBean()); // or whatever ???
    // evaluate scripts, all have the same output "abc"
    engine.eval("print(stringValue)");
    engine.eval("print(this.stringValue)");

我知道this在JavaScript中具有特殊的意义(如在Java中),但在MVEL脚本中,可以通过使用自定义ParserContext和自定义PropertyHandler来完成。

在Rhino中可能有这样的东西吗?

我试着从Pointy的回答中实现这个想法(再次感谢),但是这个解决方法对于没有this前缀的属性不起作用,这似乎是IMHO非常相同。而不是使用来自Java API的Rhino 1.5,而是来自Mozilla的原始Rhino 1.7。测试用例在这里:

import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
public class RhinoTest2 {
  private Obj obj = new Obj();
  public class Obj {
    public String getStringValue() {
      return "abc";
    }
  }
  private Object eval(String expression) {
    Context cx = Context.enter();
    try {
      ScriptableObject scope = cx.initStandardObjects();
      // convert my "this" instance to JavaScript object  
      Object jsObj = Context.javaToJS(obj, scope);
      // prepare envelope function run()    
      cx.evaluateString(scope, 
        String.format("function run() { %s }", expression), 
        "<func>", 1, null);
      // call method run()
      Object fObj = scope.get("run", scope);
      Function f = (Function) fObj;
      Object result = f.call(cx, scope, (Scriptable) jsObj, null);
      if (result instanceof Wrapper)
        return ((Wrapper) result).unwrap();
      return result;
    } finally {
      Context.exit();
    }
  }
  @Test
  public void test() {
    // works
    eval("return this.getStringValue()");
    eval("return this.stringValue");
    // doesn't work, throws EcmaError: ReferenceError: "getStringValue" is not defined.
    eval("return getStringValue()");
    eval("return stringValue");
  }
}

为什么this.getStringValue()/this.stringValue有效而getStringValue()/stringValue无效?有什么问题被忽视了吗?尖尖的?

嗯,在JavaScript中,真正有意义的是考虑在调用函数的上下文中设置this。因此,我认为您应该能够在ScriptEngine上使用"invoke"方法(必须转换为"Invocable"):

  ((Invocable) engine).invokeMethod(objectForThis, "yourFunction", arg, arg ...);

现在"objectForThis"引用(在我的经验中)通常是从先前调用"eval()"返回的东西(或"invokeMethod"我猜);换句话说,它应该是脚本引擎使用适当语言的对象。您是否可以在那里传递Java对象(并使其工作),我不确定。