如何从JavaScript调用Java实例的方法?
我正在使用Mozilla Rhino JavaScript模拟器。它允许我将Java方法添加到上下文中,然后像调用JavaScript函数一样调用它们。但我不能让它工作,除非我使用静态方法 问题在于文档的这一部分: 如果该方法不是静态的,Java“this”值将对应于JavaScript“this”值。任何试图使用不正确Java类型的“this”值调用函数的行为都会导致错误 显然,我的Java“this”值与JavaScript中的值不一致,我不知道如何使它们一致。最后,我想用Java创建一个实例,并在全局范围内安装几个方法,这样我就可以从Java初始化实例,但在脚本中使用它如何从JavaScript调用Java实例的方法?,java,javascript,rhino,method-call,Java,Javascript,Rhino,Method Call,我正在使用Mozilla Rhino JavaScript模拟器。它允许我将Java方法添加到上下文中,然后像调用JavaScript函数一样调用它们。但我不能让它工作,除非我使用静态方法 问题在于文档的这一部分: 如果该方法不是静态的,Java“this”值将对应于JavaScript“this”值。任何试图使用不正确Java类型的“this”值调用函数的行为都会导致错误 显然,我的Java“this”值与JavaScript中的值不一致,我不知道如何使它们一致。最后,我想用Java创建一个实
有人对此有一些示例代码吗?您可以做的是将Java实例绑定到Javascript上下文,然后从Javascript中,该标识符将是对“真实”Java对象的引用。然后,您可以使用它从Javascript向Java进行方法调用 Java方面:
final Bindings bindings = engine.createBindings();
bindings.put("javaObject", new YourJavaClass());
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
Javascript:
javaObject.methodName("something", "something");
现在,该示例假设您正在使用JDK 6 java.util.script API在java和Rhino之间切换。和“普通”犀牛有点不同,但基本思想是一样的
或者,您可以将Java类导入到Javascript环境中,当您对Java类的引用使用Javascript“new”时,Rhino会为您提供对Java对象的Javascript域引用。当使用Java方法时(无论是静态的还是非静态的)将在我们使用以下逻辑的范围内作为全局函数提供:
FunctionObject javascriptFunction = new FunctionObject(/* String*/ javascriptFunctionName, /* Method */ javaMethod, /*Scriptable */ parentScope);
boundScope.put(javascriptFunctionName, boundScope, javascriptFunction);
在这里,boundScope
应该始终是函数可用的范围
然而,父作用域的值取决于我们是绑定实例方法还是静态方法。对于静态方法,它可以是任何有意义的范围。它甚至可以与boundScope
相同
但是在实例方法的情况下,parentScope
应该是其方法被绑定的实例
以上只是背景信息。现在我将解释问题所在,并给出一个自然的解决方案,即允许直接作为全局函数调用实例方法,而不是显式创建对象实例,然后使用该实例调用方法
调用函数时,Rhino调用FunctionObject.call()
方法,该方法传递了对this
的引用。如果函数是一个全局函数,则调用该函数时不引用this
(即xxx()
而不是this.xxx()
),传递给FunctionObject.call()
方法的this
变量的值就是进行调用的范围(即,在这种情况下,此
参数的值将与范围
参数的值相同)
如果调用的java方法是一个实例方法,这将成为一个问题,因为根据FunctionObject
类的构造函数的JavaDocs:
如果该方法不是静态的,Javathis
值将对应于JavaScriptthis
值。任何试图使用不正确Java类型的this
值调用函数的行为都将导致错误
在上面描述的场景中,情况正是如此。javascriptthis
值与javathis
值不对应,并导致不兼容的对象错误
解决方案是子类化FunctionObject
,重写call()
方法,强制“修复”该引用,然后让调用正常进行
比如:
FunctionObject javascriptFunction = new MyFunctionObject(javascriptFunctionName, javaMethod, parentScope);
boundScope.put(javascriptFunctionName, boundScope, javascriptFunction);
private static class MyFunctionObject extends FunctionObject {
private MyFunctionObject(String name, Member methodOrConstructor, Scriptable parentScope) {
super(name, methodOrConstructor, parentScope);
}
@Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return super.call(cx, scope, getParentScope(), args);
}
}
我认为最好通过下面粘贴的一个自包含/完整的示例来理解它。在这个示例中,我们将实例方法myJavaInstanceMethod(双倍数字)公开为javascript范围(“scriptExecutionScope”)内的一个全局函数。因此,在这种情况下,“parentScope”参数的值必须是包含此方法的类的实例(即MyScriptable)
如果要查看修复程序的行为,请取消注释第78行和注释第79行:
return super.call(cx, scope, getParentScope(), args);
//return super.call(cx, scope, thisObj, args);
//return super.call(cx, scope, getParentScope(), args);
return super.call(cx, scope, thisObj, args);
如果要查看没有修复的行为,请注释第78行和取消注释第79行:
return super.call(cx, scope, getParentScope(), args);
//return super.call(cx, scope, thisObj, args);
//return super.call(cx, scope, getParentScope(), args);
return super.call(cx, scope, thisObj, args);
希望这能有所帮助。@Jawad的答案很好,但它仍然要求parentScope是您试图插入的对象的父对象。如果您想将全局函数添加到使用initStandardObjects()创建的作用域中,您必须使用共享作用域(这有点解释了,但缺少完整的示例)
以下是我是如何做到的(这是Android,请原谅Kotlin):
谢谢,这很有效。因为我直接使用Rhino API,所以代码是:global.defineProperty(“javaObject”,javaObject,ScriptableObject.CONST);
(虽然我不确定CONST
,但其他属性应用更少)。如果我想使用像[[1,3]、[4,5]、[6,9]这样的多维整数数组调用函数那么我如何传递对象[]作为params?如何从Java实例中获得可用的parentScope
?我已经用一个完整的示例更新了答案。希望有帮助。谢谢。作为一个比以前更好的答案,我想这意味着我不能把Java.lang.String
的实例放在脚本的上下文中。我总是要把它包装起来扩展了FunctionObject
。感谢@Jawad给出了这个美妙的答案。你救了我一天。两周后我就完成了。脱帽致敬