如何将JavaScript函数作为回调传递给Java方法(Rhino)

10

基本上,我想将一个JavaScript函数作为回调传递给Java方法。

我可以做到 - 但是我接收到的对象是sun.org.mozilla.javascript.internal.InterpretedFunction,我不知道如何调用它。

有什么想法吗?

这是我目前拥有的:

var someNumber = 0;

function start() {
   // log is just an log4j instance added to the Bindings
   log.info("started....");
   someNumber = 20;

    // Test is a unit test object with this method on it (taking Object as a param).
    test.callFromRhino(junk);
}

function junk() {
    log.info("called back " + someNumber);
}

这是一个小程序吗?如果不是,那么这是不可能的,因为JavaScript代码在客户端执行,而Java代码在服务器端执行。你的运行时变量在这个过程中会丢失。你需要通过POSTGET请求调用Java,将你的数据作为请求参数传递。 - FK82
@FK82 - Rhino是一个用Java编写的JavaScript解释器(它作为脚本API的一部分包含在Java 6 JVM中)。 - McDowell
@McDowell:OP没有明确指出他试图从哪个运行时调用Rhino/JavaScript函数。 - FK82
4个回答

10

实现一个接口:

import javax.script.*;

public class CallBack {
  public void invoke(Runnable runnable) {
    runnable.run();
  }

  public static void main(String[] args) throws ScriptException {
    ScriptEngine js = new ScriptEngineManager().getEngineByExtension("js");
    js.getContext().setAttribute("callBack", new CallBack(),
        ScriptContext.ENGINE_SCOPE);
    js.eval("var impl = { run: function () { print('Hello, World!'); } };\n"
        + "var runnable = new java.lang.Runnable(impl);\n"
        + "callBack.invoke(runnable);\n");
  }
}

我认为问题不是如何从Javascript调用Java方法 - 你已经给出了一个例子,而是如何将Javascript函数传递给Java方法并从Java代码中调用该函数。 - Christian Semrau
1
@Christian Semrau - 接口实现应该调用 JavaScript 函数(在我调用 print 的地方)。 - McDowell
McDowell是正确的。谢谢!方法callFromRhino可以像这样定义并且它能够正常工作!public void callFromRhino(Runnable callback) { callback.run(); } - sproketboy
现在我明白了。JavaScript 实现了一个接口(这里是 Runnable),并调用 Java 方法,该方法反过来又回调脚本实现的接口。 - Christian Semrau
参数传递怎么样? - Jiang YD

7

sun.org.mozilla.javascript.internal.InterpretedFunction 实现了接口 sun.org.mozilla.javascript.Function。该接口有一个名为 call 的方法,它需要以下参数:

  • Context 上下文
  • Scriptable 用作范围的对象
  • Scriptable 用作函数内部 this 值的对象
  • Objects 组成的数组,这些对象是传递给函数的参数

因此,我建议您在 Java 中将传递给您的对象转换为 sun.org.mozilla.javascript.Function 并调用 call 方法。前两个参数可以使用您在 Java 中启动脚本时使用的任何内容。在您的应用场景中,最后两个参数可以是 nullnew Object[0]


在Java 6的内置Rhino引擎中,sun.org.mozilla.javascript.Function不可用。我将使用可下载的rhino jar替换它。 - sproketboy
请注意,在可下载的Rhino jar中,接口被称为org.mozilla.javascript.Function - Sun在他们提供的JDK版本中重命名了所有接口。 - Daniel Martin

2
解决方案实际上是在另一个脚本中调用它。这种方法基本可行:
import javax.script.*;

public class CallFunction {

    /**
     * @param args
     * @throws Exception oops!
     */
    public static void main(String[] args) throws Exception {
        ScriptEngine js = new ScriptEngineManager().getEngineByExtension("js");
        js.getContext().setAttribute("out", System.out, ScriptContext.ENGINE_SCOPE);
        Object a = js.eval(
                "out.println('Defining function a...');" +
                "function a() {out.println('hello from JavaScript!'); }" +
                "function foobar() {out.println('in foobar() definition');}" +    
                "out.println('Done!.');"
        );

        System.out.println(js.get("a")); // InterpretedFunction
        SimpleBindings bindings = new SimpleBindings();
        bindings.put("foobar",js.get("a"));
        js.eval("foobar();", bindings); // hello from JavaScript
        js.eval("foobar();"); // in foobar() definition
    }
}

当你得到一个函数的引用时,你需要请求引擎执行该函数。虽然不太美观,但是使用特定绑定向js eval()请求执行实际上可以为你完成工作。你需要注意你正在操作的变量属于正确的作用域;我想在这里犯错误很容易。

不用谢。我曾遇到同样的问题,但没有找到对你的问题的满意答案,因此我觉得有必要提供一个有效的答案。自从我验证了这个答案确实很好用,看来回调调用Java、调用带参数函数并不难。这真的很酷!让我想知道是否可能将Node.js移植到Rhino上。哈哈。 - mogsie

1

这个例子涵盖了使用JavaScript实现Java接口,也可用于从Java调用JavaScript回调。



package com.hal.research;

import javax.script.*;

public class CallFunction {
    /**
     * 定义回调的契约
     */
    static interface WhatEverYouWant {
        public String testMe(String a, String b);
    }
    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        final ScriptEngineManager scriptManager = new ScriptEngineManager();
        final ScriptEngine js = scriptManager.getEngineByExtension("js");
        js.put("producer", new Object() {
            /**
             * @param call 是要调用的回调
             */
            public void doSomethingWithIt(WhatEverYouWant call) {
                System.out.println("调用回调JavaScript...");
                String result = call.testMe("a", "b");
                // 处理结果...
                System.out.println("调用回调...完成,结果:" + result);
            }
        });
        js.eval(  "var handler = {\"testMe\": function (a,b){return a + \" is concatenated to \"+ b;}};\n"
                + "var callback = new Packages.com.hal.research.CallFunction.WhatEverYouWant(handler);\n"
                + "producer.doSomethingWithIt(callback); ");
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接