代码之家  ›  专栏  ›  技术社区  ›  Aaron Digulla

如何从JavaScript调用Java实例的方法?

  •  9
  • Aaron Digulla  · 技术社区  · 15 年前

    我正在使用Mozilla Rhino JavaScript模拟器。它允许我将Java方法添加到上下文中,然后调用它们,就好像它们是JavaScript函数一样。但是我不能让它工作,除非我使用静态方法。

    问题在于文档的这一部分:

    如果该方法不是静态的,那么Java的“这个”值将对应于JavaScript的“这个”值。任何调用函数的值都不是正确Java类型的函数都会导致错误。

    显然,我的Java“这个”值与JavaScript中的值不符,我不知道如何使它们对应。最后,我想在Java中创建一个实例,并在全局范围内从中安装一些方法,这样我就可以从Java中初始化实例,但在脚本中使用它。

    有人有关于这个的示例代码吗?

    2 回复  |  直到 10 年前
        1
  •  9
  •   Jawad    13 年前

    当Java方法(无论是静态的还是非静态的)都作为一个全局函数在一个范围内可用时,我们使用以下逻辑:

    FunctionObject javascriptFunction = new FunctionObject(/* String*/ javascriptFunctionName, /* Method */ javaMethod, /*Scriptable */ parentScope);
    boundScope.put(javascriptFunctionName, boundScope, javascriptFunction);
    

    这里 boundScope 应始终是使功能可用的范围。

    然而,父作用域的值取决于我们是绑定实例方法还是静态方法。对于静态方法,它可以是任何有意义的范围。它甚至可以和 边界范围 .

    但在实例方法中, parentScope 应为其方法正被绑定的实例。

    以上只是背景信息。现在我将解释问题是什么,并给出一个自然的解决方案,即允许直接作为全局函数调用实例方法,而不是显式地创建对象的实例,然后使用该实例调用方法。

    调用函数时,Rhino调用 FunctionObject.call() 传递了对的引用的方法 this . 如果函数是全局函数,则调用它时不引用 (即 xxx() 而不是 this.xxx() )的值 传递给 函数对象。调用() 方法是进行调用的范围(在本例中,是 参数将与 scope 参数)。

    如果调用的Java方法是一个实例方法,那么这将成为一个问题,因为每个构造函数的JavaDoc都是 FunctionObject 班级:

    如果该方法不是静态的,则 值将对应于javascript 价值。任何使用 不是正确的Java类型的值会导致错误。

    在上面描述的场景中,情况正是如此。JavaScript 值与Java不对应 值并导致不兼容的对象错误。

    解决方案是子类 功能对象 重写 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)的类的实例。

    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);
    

    希望这有帮助。

        2
  •  3
  •   Dhananjay    10 年前

    你能做的就是绑定一个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 JavaUTILcript脚本API来获取Java和RHNOIO之间的关系。从“普通”犀牛,它有点不同,但基本的想法是相同的。

    或者,您可以将Java类导入到JavaScript环境中,当JavaScript“新”引用Java类时,R犀o向Java对象提供JavaScript域引用。