我是靠谱客的博主 迷人小刺猬,最近开发中收集的这篇文章主要介绍nashorn 和 delight-nashorn-sandbox 学习笔记一、java的脚本执行引擎ScriptEngine二、Nashorn JavaScript 引擎三、nashorn的sandbox的使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、java的脚本执行引擎ScriptEngine

从JDK6开始,java就嵌入了对脚本的支持,这里的脚本指的是但非局限于JS这样的非java语言,当时使用的脚本执行引擎是基于Mozilla 的Rhino。该引擎的特性允许开发人员将 JavaScript 代码嵌入到 Java 中,甚至从嵌入的 JavaScript 中调用 Java。此外,它还提供了使用jrunscript从命令行运行 JavaScript 的能力。

Java ScriptEngine优缺点:

优点:可以执行完整的JS方法,并且获取返回值;在虚拟的Context中执行,无法调用系统操作和IO操作,非常安全;可以有多种优化方式,可以预编译,编译后可以复用,效率接近原生Java;所有实现ScriptEngine接口的语言都可以使用,并不仅限于JS,如Groovy,Ruby等语言都可以动态执行。

缺点:无法调用系统和IO操作 ,也不能使用相关js库,只能使用js的标准语法。更新:可以使用scriptengine.put()将Java原生Object传入Context,从而拓展实现调用系统和IO等操作。

二、Nashorn JavaScript 引擎

从JDK 8开始,Nashorn取代Rhino成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的invokedynamic,将JavaScript编译成Java字节码。

nashorn首先编译javascript代码为java字节码,然后运行在jvm上,底层也是使用invokedynamic命令来执行,所以运行速度很给力。

Nashorn是一个纯编译的JavaScript引擎。它没有用Java实现的JavaScript解释器,而只有把JavaScript编译为Java字节码再交由JVM执行这一种流程,跟Rhino的编译流程类似。

[ JavaScript源码 ] -> ( 语法分析器 Parser ) -> [ 抽象语法树(AST) ir ] -> ( 编译优化 Compiler ) -> [ 优化后的AST + Java Class文件(包含Java字节码) ] -> JVM加载和执行生成的字节码 -> [ 运行结果 ]

只从JVM以上的层面看,Nashorn是一种单层的纯编译型JavaScript实现。所有JavaScript代码在首次实际执行前都会被编译为Java字节码交由JVM执行。(当然JVM自身可能是混合执行模式的,例如HotSpot VM与J9 VM。所以Nashorn在实际运行中可能需要一定预热才会达到最高速度)

1. java代码中使用 nashorn

为了在java中执行JavaScript代码,首先使用原先Rhino (旧版Java1.6中来自Mozilla的引擎)中的包javax.script来创建一个nashorn脚本引擎。

把JavaScript代码作为一个字符串来直接执行,也可放入一个js脚本文件中

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('Hello World!');");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("script.js"));

2. 向Java传递数据或者从Java传出数据

可以将数据作为字符串显式传递

ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); 
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn"); 
String name = "Olli"; 
nashorn.eval("print('" + name + "')");

可以在Java中传递绑定,它们是可以从JavaScript引擎内部访问的全局变量

int valueIn = 10; 
SimpleBindings simpleBindings = new SimpleBindings(); 
simpleBindings.put("globalValue", valueIn); 
nashorn.eval("print (globalValue)", simpleBindings);

JavaScript eval的求值结果将会从引擎的“eval”方法返回

Integer result = (Integer) nashorn.eval("1 + 2"); 
assert(result == 3);

2.1 在 JavaScript 端调用 Java 方法

在 JavaScript 中调用 Java 方法很简单。首先我们定义一个静态的 Java 方法:

static String fun1(String name) {
    System.out.format("Hi there from Java, %s", name);
    return "greetings from java";
}

JavaScript 可通过 Java.type API 来引用 Java 类。这跟在 Java 类中引入其他类是类似的。当定义了 Java 类型后我们可直接调用其静态方法 fun1() 并打印结果到 sout。因为方法是静态的,所以我们无需创建类实例。

var MyJavaClass = Java.type('my.package.MyJavaClass');

var result = MyJavaClass.fun1('John Doe');
print(result);

三、nashorn的sandbox的使用

java提供脚本支持,这给我们业务提供了便利的同时,也给我们的服务带来了更大的风险,因为如果我们的业务需求是提供执行脚本的接口,那么JS脚本就是由客户端输入,这存在很多的不确定性,存在安全隐患,比如:

  • js代码存在死循环
  • js代码可以操作宿主机上面的功能,删除机器上的文件
  • js执行占用过多的java资源

这个时候sandbox就应运而生了,sandbox的作用就是将JS脚本执行的环境独立出来,达到对java类的访问限制以及对Nashorn引擎的资源限制的目的。

1、创建sandbox

导包:

<dependency>
    <groupId>org.javadelight</groupId>
    <artifactId>delight-nashorn-sandbox</artifactId>
    <version>0.2.5</version>
</dependency>

创建sandbox:

NashornSandbox sandbox = NashornSandboxes.create();

配置sandbox:

sandbox.setMaxCPUTime(100);// 设置脚本执行允许的最大CPU时间(以毫秒为单位),超过则会报异常,防止死循环脚本
sandbox.setMaxMemory(1024 * 1024); //设置JS执行程序线程可以分配的最大内存(以字节为单位),超过会报ScriptMemoryAbuseException错误
sandbox.allowNoBraces(false); // 是否允许使用大括号
sandbox.allowLoadFunctions(true); // 是否允许nashorn加载全局函数
sandbox.setMaxPreparedStatements(30); // because preparing scripts for execution is expensive // LRU初缓存的初始化大小,默认为0
sandbox.setExecutor(Executors.newSingleThreadExecutor());// 指定执行程序服务,该服务用于在CPU时间运行脚本

2、sandbox的使用

eval方法:

​ 在sandbox中,使用eval可以直接编译执行JS脚本,但是,如果我们的JS脚本是一个完整的function方法,那么eval方法会编译该方法但是并不会去执行:

在这里插入图片描述

可以看到,我们的JS方法中有一个return ‘Hello Word !’的代码,正常执行的话,应该是有一个“Hello Word”的返回值的,但是我们看输出结果,返回值为true,说明我们的JS代码语法没有问题,被正常的编译进入sandbox中,但是我们并没有去调用执行该方法,那么我们把脚本代码稍微修改一下:

在这里插入图片描述

我们在方法的后面跟了一个test(),这相当于我们对test方法进行了调用,所以这次我们的返回值是我们预想的结果:Hello Word!

  • 直接使用eval执行无参数JS代码

    sandbox.eval("1 + 1");
    

在这里插入图片描述

  • 使用eval执行有参数的JS代码

    Bindings bindings = sandbox.createBindings();
    bindings.put("a", 1);
    bindings.put("b", 2);
    bindings.put("c", 2);
    sandbox.eval("function calFunction(a, b, c){return a+b+c;}", bindings);
    

在这里插入图片描述

  • 使用compile先编译无参脚本,再使用eval执行

    刚才上面有说到nashorn执行JS的原理,当我们传入JS脚本时,nashorn会将JS脚本编译成JVM能够识别的字节码文件,然后交由JVM去进行执行。

    [在这插一句:如果大家有精力的话可以去了解一下JVM的动态编译,像我们平时使用IDEA启动JAVA服务,都是现将JAVA项目文件编译为.class文件以后再去执行,这就是所谓的静态编译。动态编译是指服务在运行的过程中,实时编译代码交由JVM进行执行(大致是这么个意思,详情目前了解也不是很透彻)。而nashorn的原理就跟java的动态编译很类似,是在服务运行过程中去编译文件进行执行]

    而编译是比较消耗资源和时间的,所以nashorn中也提供了编译的机制,一次编译多次执行。

    CompiledScript compile = sandbox.compile("111+111");
    compile.eval()
    
  • 使用compile先编译有参脚本,再使用eval执行

    Bindings bindings = sandbox.createBindings();
    bindings.put("a", 1);
    bindings.put("b", 2);
    bindings.put("c", 2);
    CompiledScript compile = sandbox.compile("function calFunction(a, b, c){return a+b+c;};calFunction(a,b,c)");
    compile.eval(bindings)
    

在这里插入图片描述

3、sandbox进阶使用

  • 在JS脚本中使用java中的类

    刚才我们有说到,sandbox限制了我们对java类的引用,但是sandbox又很人性化的给我们留了后门,在配置sandbox时,我们可以使用allow()方法来允许sandbox可以使用java的某个类。

sandbox.allow(StringBuffer.class)

在未使用 allow之前:

在这里插入图片描述

在使用之后:

在这里插入图片描述

在这个例子中,我们在JS中是使用Java.type()方法来得到Java类的对象并使用。需要注意的是,如果想在JS中使用java类的方法,前提是java类中的方法必须是静态的。

在这里插入图片描述

这是githup中,nashornSandBox项目的对JS执行JAVA对象的解释

  • 在方法中引用别的方法

JS执行引擎的局限性还是蛮大的,总体感觉就是一个加工厂,往工厂中加入原料(传入JS脚本),然后加工厂对原料进行加工(编译),最后生成产品返回(执行并返回值)。如果说,我们在JS脚本中使用JS的语法规范引入别的包或者方法,那么JS执行引擎就傻嘚了,他不知道该去哪里找这个包或者说这个方法,那么执行就会报错。

在这里插入图片描述

当然,有问题那么就有解决办法。在nashorn中,JS脚本是允许预编译的,一次编译多次使用。按照这个思路,我们完全可以先把方法要引用的其他方法先一步编译到JS执行引擎中,这样在执行方法时就能直接找到预编译好的方法进行调用,那么我们来改一下上面这个例子:

在这里插入图片描述

同样的test方法,不过我先一步将他要使用的test2方法编译到执行器中,这样就能执行成功了

但是,需要注意的是,在JS调用别的方法,方法名称是作为唯一标识的,如果往执行器中编译两个同名的方法,后者的方法会覆盖前者的方法

在这里插入图片描述

  • 预编译方法的调用

在上面的例子中我们看到,JS脚本是可以提前预编译到JS执行器中,后续方法可以直接调用。但是,在上面的所有例子中,我们执行方法都是使用的eval方法,并且在JS脚本中去做调用:

在这里插入图片描述

这种方式是不合理的,如果我们只是想编译,并不想执行,那么这样就会去做多余操作。但是如果不加这个,我们后续又该怎么去执行已经编译在JS引擎中的方法呢?

在这里sandbox提供了.getSandboxedInvocable().invokeFunction()方法,这个方法可以直接通过方法名称去执行已经编译在执行器中的方法:

在这里插入图片描述

能读完也是蛮厉害的,给你点个赞!!!!

本文属于自学笔记,如果内容中有什么不对的地方,欢迎各位留言吐槽!!

最后

以上就是迷人小刺猬为你收集整理的nashorn 和 delight-nashorn-sandbox 学习笔记一、java的脚本执行引擎ScriptEngine二、Nashorn JavaScript 引擎三、nashorn的sandbox的使用的全部内容,希望文章能够帮你解决nashorn 和 delight-nashorn-sandbox 学习笔记一、java的脚本执行引擎ScriptEngine二、Nashorn JavaScript 引擎三、nashorn的sandbox的使用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部