Rhino:如何将Java对象传递给脚本,其中可以引用为;这";

Rhino:如何将Java对象传递给脚本,其中可以引用为;这";,java,javascript,rhino,Java,Javascript,Rhino,我是JSR-223Java脚本的新手,实际上我正在从标准转换到标准。我已经阅读了所有的文档,但是被卡住了。我尝试通过绑定从脚本中引用一些Java对象,就像在教程中一样: // my object public class MyBean { public String getStringValue() { return "abc" }; } // initialization ScriptEngineManager manager = new

我是JSR-223Java脚本的新手,实际上我正在从标准转换到标准。我已经阅读了所有的文档,但是被卡住了。我尝试通过绑定从脚本中引用一些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)");
我知道JavaScript中的
这个
有着特殊的意义(就像在Java中一样),但是在MVEL脚本中,可以使用custom和custom来完成

在Rhino中可能出现类似情况吗?


非常感谢。

在JavaScript中,只有在调用函数的上下文中设置
才有意义。因此,我认为您应该能够在ScriptEngine上使用“invoke”方法(必须转换为“Invocable”):

根据我的经验,“objectForThis”引用通常是从先前调用“eval()”(我猜是“invokeMethod”)返回的;换句话说,它应该是脚本引擎的适当语言中的对象。您是否可以在那里传入一个Java对象(并将其解决),我不确定。

我尝试从Pointy的答案实现它(再次感谢),但是如果没有
this
前缀,这个解决方法就不适用于属性,因为它看起来非常相似。它不是使用JavaAPI中的Rhino1.5,而是Mozilla中的Rhino1.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");
  }
}
import org.junit.Test;
导入org.mozilla.javascript.Context;
导入org.mozilla.javascript.Function;
导入org.mozilla.javascript.Scriptable;
导入org.mozilla.javascript.ScriptableObject;
导入org.mozilla.javascript.Wrapper;
公共类RhinoTest2{
专用Obj Obj=新Obj();
公共类Obj{
公共字符串getStringValue(){
返回“abc”;
}
}
私有对象求值(字符串表达式){
Context cx=Context.enter();
试一试{
ScriptableObject范围=cx.initStandardObjects();
//将我的“this”实例转换为JavaScript对象
objectjsobj=Context.javaToJS(obj,scope);
//准备信封函数运行()
cx.evaluateString(范围,
format(“函数运行(){%s}”,表达式),
“”,1,空);
//调用方法run()
对象fObj=scope.get(“run”,scope);
函数f=(函数)fObj;
对象结果=f.call(cx,范围,(可编写脚本的)jsObj,null);
if(包装器的结果实例)
返回((包装器)结果).unwrap();
返回结果;
}最后{
Context.exit();
}
}
@试验
公开无效测试(){
//工作
eval(“返回this.getStringValue()”;
eval(“返回此.stringValue”);
//不工作,抛出EcmaError:ReferenceError:“getStringValue”未定义。
eval(“return getStringValue()”;
评估(“返回值”);
}
}

为什么
this.getStringValue()/this.stringValue
有效而
getStringValue()/stringValue
无效?你忽略了一些要点吗?尖头?

谢谢,正是我需要的!实际上,我必须用
“function run(){%s}”
包装用户脚本,然后按照您的建议通过invokeMethod()调用
run
,而
这个
适合我。一个不便之处是,用户脚本必须严格使用
return
关键字——eval()脚本返回的结果也没有
return
关键字——但没关系,它比另一种解决方案要好得多。哦,不完全是这样。遗憾的是,它只适用于前缀为
的表达式。也就是说,
print(“this.stringValue”)
有效,但
print(“stringValue”)
无效。??嗯?我不明白那是什么意思。这就像在Javascript中使用“.call()”方法一样——它调用绑定到对象的
this
函数。如果“stringValue”是
这个
对象上的属性名,那么JavaScript就是这样工作的,不管Rhino与否。请在单独的回答中查看测试用例——超过了注释长度,对不起。因为JavaScript根本不是这样工作的。在任何情况下,
this
的值都不会被假定为参考基准。JavaScript在这方面与Java不同(与其他许多语言一样)。如果您想要
this
的属性,则必须显式引用它(或引用值的副本或类似内容)。好的,再次感谢。然而,如果MVEL不稳定且支持率相对较低,那么它比Rhino更容易与Java应用程序集成。JavaScript只是一种不同的动物。。。
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");
  }
}