如何使用Rhino将Java类中的方法添加为JavaScript全局函数?

13

我有一个简单的 Java 类,其中包含一些方法:

public class Utils {
    public void deal(String price, int amount) {
        // ....
    }
    public void bid(String price, int amount) {
        // ....
    }
    public void offer(String price, int amount) {
        // ....
    }
}

我想创建该类的一个实例,并允许JavaScript代码直接调用方法,如下所示:

deal("1.3736", 100000);
bid("1.3735", 500000);

目前我能想到的唯一方法是使用

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.put("utils", new Utils());

然后在Javascript代码中使用 utils.deal(...) 。 我也可以为每个方法在Javascript中编写包装函数,但应该有一种更简单的方式来自动处理类的所有公共方法。


你应该意识到Java和JavaScript是两种完全不同的编程语言。它们可能有类似C语言的语法,但它们并不相同。 - thecoshman
1
Rhino是Java的嵌入式js引擎。这种事情应该是可以实现的。我用SpiderMonkey在Perl中成功实现了它:http://blog.dorward.me.uk/2006/02/02/spambots_that_drink_coffee.html - Quentin
3
这是一个非常合理的问题。你可以在Java中运行JavaScript,而且有时你可能需要在JS中使用Java的好东西。 - lexicore
4个回答

7

我不太熟悉Rhino,但是类似这样的代码应该可以实现:

for(var fn in utils) {
  if(typeof utils[fn] === 'function') {
    this[fn] = (function() {
      var method = utils[fn];
      return function() {
         return method.apply(utils,arguments);
      };
    })();
  }
}

只需遍历utils的属性,对于每个是函数的属性,创建一个调用它的全局函数。

编辑:我在Groovy脚本中实现了这个功能,但我必须将utils设置在绑定中,而不是像你的代码一样设置在引擎中:

import javax.script.*

class Utils {
   void foo(String bar) {
      println bar
   }   
}

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");

engine.eval("""
for(var fn in utils) {
  if(typeof utils[fn] === 'function') {
    this[fn] = (function() {
      var method = utils[fn];
      return function() {
         return method.apply(utils,arguments);
      };
    })();
  }
}

foo('foo'); // prints foo, sure enough
""",new SimpleBindings("utils":new Utils()))

赞一个,闭包使用很好。我想我会采用你的解决方案。 - elifiner
你会如何在Java中使其工作?你会使用以下代码吗:ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); engine.put("utils", new Utils()); 还是其他方法? - Anderson Green

4

我不确定如何使用JSR-223 API实现这个功能,但是使用Rhino API,您可以像这样创建一个FunctionObject并添加想要的方法。

Class[] parameters = new Class[] { String.class, Integer.class };
Method dealMethod = Utils.class.getMethod("deal", parameters);
engine.put("deal", new FunctionObject("deal", dealMethod, scope));

文档可在https://www.mozilla.org/rhino/apidocs/org/mozilla/javascript/FunctionObject.html获取。
您可能需要引用Rhino库才能访问FunctionObject类,并且我不确定如何使用JSR-223 API获取scope对象(尽管,可能null会起作用)。

我不认为这会起作用,因为交易函数对象没有引用到实例。 - Geoff Reedy
@Geoff:我一开始以为它们是静态方法,因为它们可以在没有(JavaScript)this对象的情况下被调用。再看一遍,我猜它们不是静态方法,但可能应该是。 - Matthew Crumley
1
谢谢。实际上,在使用Rhino API时,Context类有一个javaToJS方法,正好符合我的需求。看起来它也适用于非静态方法。我本来希望有一个JSR-223的解决方案,但我认为没有这样的解决方案。 - elifiner
谢谢,Rhino文档在这一部分过于不清晰。 - Rémy DAVID

0

0

使用Java Lambdas(自1.8版本以来)实际上可以只做到这一点:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
BiConsumer<String, Integer> deal = Utils::deal
engine.put("deal", deal);

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