• home > webfront > browser > webkit >

    JS引擎(3):java项目里面的JS业务集成—Rhino/Nashorn

    Author:zhoulujun Date:

    java与JavaScript交互,有两种引擎,Rhino、Nashorn。从根本上讲在 Java 项目中嵌入 JavaScript 脚本引擎,最重要的一点是实现 Java 和 JavaScript 之间的数据共享。Rhino为我们提供了强大的Java 和 JavaScript 交互的方法。

    初步整理,内容非常杂乱,待完善……

    Mozilla Rhino是一个纯JavaScript语言的实现;而Node.js是一套JavaScript程序的运行时环境,不但包括由V8提供的JavaScript核心语言的实现,还包括丰富的库,最有特点的就是其基于事件的I/O库。用一句话描述Node.js就是其广告词之一:“Evented I/O for V8 JavaScript”。、

    基于JVM的语言实现是服务器端程序的流行选择之一,而Rhino正是用Java实现、运行在JVM上的JavaScript引擎,可以无缝使用Java丰富的核心库和第三方库,所以有不少基于Rhino的服务器端JavaScript解决方案。

    Rhino只是一个Java语言写JavaScript引擎,可以执行JavaScript代码而已。但是平时,我们部分逻辑业务,用rhino做,还是不成问题的

    Java 和 JavaScript 交互

    java与JavaScript交互,有两种引擎,Rhino、Nashorn

    从根本上讲在 Java 项目中嵌入 JavaScript 脚本引擎,最重要的一点是实现 Java 和 JavaScript 之间的数据共享。Rhino为我们提供了强大的Java 和 JavaScript 交互的方法。

    Rhino脚本引擎主要的类图

    Rhino脚本引擎主要的类图

    ScriptEngine接口就是脚本引擎,用于执行脚本计算结果的接口,其实现类是AbstractScriptEngine和底层的RhinoScriptEngine,这些类是脚本引擎的核心类。eval(String script)和eval(String script, Bindings n)两个方法就是执行一段脚本返回计算结果的两个方法,第二个方法会传入上下文,即运行脚本时,脚本需要使用上下文中设置的Java变量的方法或属性。

    上下文是有范围的,分三种范围:

    • 全局的:所有脚本引擎都可以使用的,由ScriptContext. GLOBAL_SCOPE常量来定义其范围的名称。ScriptEngine.getBindings(ScriptContext.GLOBAL_SCOPE)可以获GLOBAL取这类上下文。

    • 引擎即的:只是一个脚本引擎可以使用的,由ScriptContext. ENGINE_SCOPE常量来定义其范围的名称。ScriptEngine.getBindings(ScriptContext.ENGINE_SCOPE)可以获取这类上下文。

    • 局部的:引擎的一次计算用到的Bindings,没有常量定义,ScriptEngine.createBindings()创建的就是这类上下文。


    Java中使用JavaScript脚本-Nashorn 

    创建JavaScript容器用户存储脚本 ScirptContainer.java

    public class ScirptContainer {
    
     public static ScriptEngine engine;//脚本引擎
     
     static {
      ScriptEngineManager manager = new ScriptEngineManager();//脚本引擎管理
      engine = manager.getEngineByName("nashorn");//获取nashorn脚本引擎
      engine.getContext().getWriter();//获取正文并且写入
     }
     
     private ConcurrentHashMap<Integer, CompiledScript> scripts = new ConcurrentHashMap<>();//脚本存储容器
     
     public CompiledScript getCompiledScript(String script) throws ScriptException{
      //判断脚本是否为空
      if(script == null || "".equals(script)){
       throw new ScriptException("JavaScript empty");
      }
      //获取脚本Hash
      int hashCode = script.hashCode();
      //从容器中获取脚本
      CompiledScript compiledScript = scripts.get(hashCode);
      if(compiledScript == null){
       //容器中无脚本创建脚本对象
       Compilable compilable = (Compilable) engine;
       //编译JavaScript脚本
       compiledScript = compilable.compile(script);
       //脚本对象存入容器中
       scripts.put(hashCode, compiledScript);
      }
      return compiledScript;
     }
     
    }

    Java执行JavaScript脚本

    public class ScriptHandler {
     
     //创建容器对象
     private ScirptContainer scirptContainer = new ScirptContainer();
     
     //需要执行的对象
     String js1 = "function scriptFunction(obj){  var a = 1; var b = 2; return obj.sum(a,b); } scriptFunction(obj);";
    
     @Test
     public void test() throws ScriptException{
      //获取脚本对象
      CompiledScript c1 = scirptContainer.getCompiledScript(js1);
      //创建参数绑定
      Bindings bindings = scirptContainer.engine.createBindings();
      //obj参数绑定SumTest类
      bindings.put("obj", new SumTest());
      //执行JavaScript脚本并且打印返回值
      System.out.println(c1.eval(bindings));
     }
    }

    注意事项:

    1. 脚本中scriptFunction(obj);是必须存在,否则不会执行方法.

    2. 脚本中可以创建Java对象,需要全类名如var map = new java.util.HashMap();

    在 Java 中获取 JavaScript 脚本中的变量

    可以用于获取脚本运行过程中的一些临时信息变量

    Context ct = Context.enter();
    Scriptable scope = ct.initStandardObjects();
    ct.evaluateString(scope, "var test = 'Successful';", null, 1, null);
    Object jsObject = scope.get("test" , scope);
    if (jsObject == Scriptable.NOT_FOUND) {
       System.out.println("test is not defined.");
    } else {
       System.out.println("test is " + Context.toString(jsObject));
    }

    在上述的示例代码中,我们同样用到了 scope 变量,这个是 JavaScript 运行时的全局变量,你可以将它理解成为一个容器,里面包含了 JavaScript 运行过程中的所有信息,所以如果你希望取得 JavaScript 过程中的某些信息,应首先考虑该对象。

    在 Java 中调用 JavaScript 脚本中的函数

    在前文中介绍了如何在 JavaScript 脚本运行Java函数,同样我们也可以在Java代码中运行JavaScript中的函数,示例代码如下:

    Context ct = Context.enter();
    Scriptable scope = ct.initStandardObjects();
    ct.evaluateString(scope,"function test(name){return 'Successful!'+name;}", null, 1, null);
    Object functionObject = scope.get("test" , scope);
    if (!(functionObject instanceof Function)) {
        System.out.println("test is undefined or not a function.");
    } else {
        Object testArgs[] = {"Ceven"};
        Function test = (Function)functionObject;
        Object result = test.call(ct, scope, scope, testArgs);
        System.out.println(Context.toString(result));
    }



    JavaScript中使用Java的功能

    将一部分功能用 Java 实现,并在JavaScript中调用,可以方便解决java转换到JavaScript产生的个别问题,示例代码如下:

    Context ct = Context.enter();
    Scriptable scope = ct.initStandardObjects();
    String str = "var test={};";
    str += "test.call=function(){return 'Successful!';};";
    str += "java.lang.System.out.println(test.call())";
    ct.evaluateString(scope, str, null, 1, null);


    可以在JavaScript使用Rhino提供的importPackage和importClass,详见文末链接

    将Java对象共享到JavaScript

    可以将 Java 对象转换为 JavaScript 对象,并用putProperty方法添加到运行环境中,示例代码如下:

    Context ct = Context.enter();
    Scriptable scope = ct.initStandardObjects();
    Object out = Context.javaToJS(System.out, scope);
    ScriptableObject.putProperty(scope, "out", out);
    ct.evaluateString(scope, "out.println('Successful!')", null, 1, null);


    关于Java跑Rhino的文章:

    https://www.tutorialspoint.com/java8/java8_nashorn_java_script.htm

    https://stackoverflow.com/questions/22502630/switching-from-rhino-to-nashorn

    https://blogs.oracle.com/java-platform-group/nashorn,-the-rhino-in-the-room

    Rhino -- 基于java的javascript实现 https://www.cnblogs.com/cczw/archive/2012/07/16/2593957.html

    Rhino初试 https://www.jianshu.com/p/689edaa07a98

    Java中使用JavaScript脚本 https://www.debug8.com/java/t_40597.html


    转载本站文章《JS引擎(3):java项目里面的JS业务集成—Rhino/Nashorn》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/browser/webkit/2020_0719_8525.html