当要在范围内将java方法(静态或非静态)用作全局函数时,我们使用以下逻辑:
FunctionObject javascriptFunction = new FunctionObject( javascriptFunctionName, javaMethod, parentScope);boundScope.put(javascriptFunctionName, boundScope, javascriptFunction);
在这里,
boundScope应该始终是要提供该功能的范围。
但是,父作用域的值取决于我们是绑定实例方法还是静态方法。对于静态方法,可以是任何有意义的范围。甚至可以与相同
boundScope。
但是在使用实例方法的情况下,
parentScope应该是绑定了方法的实例。
以上只是背景信息。现在,我将说明问题所在,并给出一个自然的解决方案,即允许直接将实例方法作为全局函数调用,而不是显式创建对象的实例,然后使用该实例调用方法的解决方案。
调用函数时,Rhino将调用
FunctionObject.call()传递给的引用的方法
this。如果函数是全局函数,则在不引用
this(即
xxx()代替
this.xxx())的情况下调用该函数,
this传递给该
FunctionObject.call()方法的变量的值就是进行调用的范围(即,在这种情况下,
this参数将与参数的值相同
scope)。
如果被调用的java方法是一个实例方法,这将成为一个问题,因为根据
FunctionObject类的构造函数的JavaDocs :
如果该方法不是静态的,则Java
this值将对应于Javascript
this值。尝试使用
this不正确的Java类型的值来调用该函数将导致错误。
在上述情况下,情况确实如此。javascript
this值与java
this值不对应,并导致不兼容的对象错误。
解决方案是子类化
FunctionObject,重写
call()方法,强行“固定”
this引用,然后让调用正常进行。
所以像:
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(Double
number)公开为javascript范围(’scriptExecutionScope’)中的全局函数。因此,在这种情况下,“
parentScope”参数的值必须是包含此方法的类的实例(即Myscriptable)。
package test;import org.mozilla.javascript.*;import java.lang.reflect.Member;import java.lang.reflect.Method;//-- This is the class whose instance method will be made available in a Javascript scope as a global function.//-- It extends from scriptableObject because instance methods of only scriptable objects can be directly exposed//-- in a js scope as a global function.public class Myscriptable extends scriptableObject { public static void main(String args[]) throws Exception { Context.enter(); try { //-- Create a top-level scope in which we will execute a simple test script to test if things are working or not. scriptable scriptExecutionScope = new importerTopLevel(Context.getCurrentContext()); //-- Create an instance of the class whose instance method is to be made available in javascript as a global function. scriptable myscriptable = new Myscriptable(); //-- This is not strictly required but it is a good practice to set the parent of all scriptable objects //-- except in case of a top-level scriptable. myscriptable.setParentScope(scriptExecutionScope); //-- Get a reference to the instance method this is to be made available in javascript as a global function. Method scriptableInstanceMethod = Myscriptable.class.getMethod("myJavaInstanceMethod", new Class[]{Double.class}); //-- Choose a name to be used for invoking the above instance method from within javascript. String javascriptFunctionName = "myJavascriptGlobalFunction"; //-- Create the FunctionObject that binds the above function name to the instance method. FunctionObject scriptableInstanceMethodBoundJavascriptFunction = new MyFunctionObject(javascriptFunctionName, scriptableInstanceMethod, myscriptable); //-- Make it accessible within the scriptExecutionScope. scriptExecutionScope.put(javascriptFunctionName, scriptExecutionScope, scriptableInstanceMethodBoundJavascriptFunction); //-- Define a simple test script to test if things are working or not. String testscript = "function simpleJavascriptFunction() {" + " try {" + " result = myJavascriptGlobalFunction(12.34);" + " java.lang.System.out.println(result);" + " }" + " catch(e) {" + " throw e;" + " }" + "}" + "simpleJavascriptFunction();"; //-- Compile the test script. script compiledscript = Context.getCurrentContext().compileString(testscript, "My Test script", 1, null); //-- Execute the test script. compiledscript.exec(Context.getCurrentContext(), scriptExecutionScope); } catch (Exception e) { throw e; } finally { Context.exit(); } } public Double myJavaInstanceMethod(Double number) { return number * 2.0d; } @Override public String getClassName() { return getClass().getName(); } 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);// return super.call(cx, scope, thisObj, args); } }}如果要查看修复的行为,请取消注释行78和注释行79:
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);
希望这可以帮助。



