当前位置: 首页 > 面试题库 >

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

姬心思
2023-03-14
问题内容

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

问题是文档的这一部分:

如果该方法不是静态的,则Java的“ this”值将对应于JavaScript的“
this”值。任何使用不正确的Java类型的’this’值调用该函数的尝试都会导致错误。

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

有人为此提供一些示例代码吗?


问题答案:

当要在范围内将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())的情况下调用该函数,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);

希望这可以帮助。



 类似资料:
  • 问题内容: 我试图将一些Scala代码注入到现有的Java应用程序中。(所以,话说回来,我想要更多的乐趣)。 我在Scala中创建一个单例的东西 现在,在OldJava.java中 我应该填写什么以便Java调用showMyPower方法?我尝试了两者 ,但没有任何效果。 (使用Jad反编译类文件,除了胡说八道之外,什么都没给我显示。) 编辑 我删除了声明,scala产生了预期的静态方法。(打电话

  • 问题内容: 我已经创建了一个applet,并且将要从Web项目上的HTML页面访问applet方法。 这里我的小程序看起来像: 我的HTML页面如下所示: 但是,当我单击单选按钮时,浏览器将挂起,并且无法访问applet方法。 我的applet类位于默认目录中,而HTML位于WebContent文件夹中。我应该更改我的代码吗? 问题答案: 问题是语句检查: 这与JavaScript不完全一样,因为

  • 所以我希望fxml文件中的这段代码实际上会调用sceneHandeler中的一个名为setBackground的方法,但它给了我一个错误:

  • 问题内容: 我正在使用MVC3架构,c#.net。当焦点更改到下一个字段(即密码字段)时,我需要立即将文本框内容(用户ID)与数据库进行比较。因此,我想对用户ID字段使用onblur事件,然后再调用Controller方法。有谁能告诉我如何解决这个问题?作为我的新手,代码片段受到高度赞赏。 提前致谢, 普拉山斯 问题答案: 这是一个例子。控制器方法示例 这将是您的javascript函数。

  • 我创建了一个类,该类旨在获取2个分数,每个分数都有一个分子和分母,然后将它们相加,输出另一个分数。 当我编译程序时,我遇到了一个问题,涉及: 分数F3=新分数。添加(F1、F2);在主要方法中 错误:找不到类型分数$add 如果我将所有内容都设置为一个类,那么程序将运行,但我希望所有方法都严格位于Fraction类中,并在UseFraction类中调用Fraction。

  • 问题内容: 是否可以从Javascript调用Java(GWT)方法?从文档中也不清楚。http://code.google.com/intl/zh- CN/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html上的 所有示例都演示了如何从JSNI(而非JS)函数调用Java函数。 更新1 这是一个Java代码: 这是html中的呼叫者按钮示例: 这