jdk nashorn

从JDK 6开始,Java附带了基于Mozilla的Rhino的捆绑JavaScript引擎 。 此功能使您可以将JavaScript代码嵌入Java,甚至可以从嵌入式JavaScript调用Java。 此外,它还提供了使用jrunscript从命令行运行JavaScript的功能。 如果您不需要出色的性能,并且可以使用有限的ECMAScript 3功能集,那将非常不错。

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

尽管仍比Chrome和Node.js内的引擎V8稍差一点,但与以前的Rhino实施相比,它的性能提高了2到10倍。 如果您对实现的细节感兴趣,可以查看2013 JVM Language Summit的这些幻灯片。

由于Nashorn随JDK 8一起提供,它还为功能接口增加了非常简洁的支持,我们将在稍后详细介绍。

让我们从一个非常小的例子开始。 首先,您可能要安装JDK 8和NetBeans,IntelliJ IDEA或Eclipse。 所有这些都至少为集成JavaScript开发提供了基本支持。 让我们创建一个由以下两个示例文件组成的简单Java项目,并让程序运行:

(点击图片放大)

在第12行中,我们使用引擎的“ eval”方法评估任何JavaScript代码。 在这种情况下,我们只加载顶部JavaScript文件并对其进行评估。 您可能会发现“打印”不熟悉。 它不是JavaScript的内置函数,但是Nashorn提供了此功能以及其他在脚本环境中派上用场的便捷功能 。 您也可以将“ hello world”的打印直接嵌入到传递给“ eval”方法的字符串中,但是将JavaScript放在自己的文件中将为它打开整个工具世界。

Eclipse当前没有通过其JavaScript开发工具 (JSDT)项目提供专用的Nashorn支持,但是,支持JavaScript的基本工具和编辑:

(点击图片放大)

IntelliJ IDEA 13.1(社区版和终极版)提供了出色JavaScript和Nashorn支持。 有一个功能齐全的调试器,它甚至允许重构在Java和JavaScript之间进行同步。 因此,例如,如果您重命名从JavaScript引用的Java类或重命名从Java引用JavaScript文件,则IDE会跨语言修改相应的引用。

这是如何调试从Java调用JavaScript的示例(请注意,NetBeans还提供JavaScript调试,如下面的屏幕快照所示):

(点击图片放大)

您可能会说该工具看起来不错,新的实现可以修复性能以及合规性问题,但是为什么要使用它呢? 原因之一是通用脚本。 有时候,它可以派上用场,并且可以让它被解释。 有时,最好不要使用编译器,或者不担心静态类型。 也许您对Node.js编程模型感兴趣,该模型可与Java一起使用,正如我们在本文结尾处所看到的。 还有一种情况表明,使用JavaScript而不是Java可以更快地开发JavaFX。

Shell脚本

可以使用jjs命令从命令行调用Nashorn引擎。 您可以不带任何参数地调用它,这将使您进入交互模式,或者可以传递要执行JavaScript文件的名称,也可以使用它代替Shell脚本,如下所示:

#!/usr/bin/env jjsvar name = $ARG[0]; print(name ? "Hello, ${name}!" : "Hello, world!"); 

要将程序参数传递给jjs,请在它们前面加上“-”。 因此,例如,您可以调用:

./hello-script.js -- Joe

如果没有前缀“-”,则该参数将被解释为文件名。

在Java和Java之间传递数据

如上所述,您可以直接从Java代码调用JavaScript; 只需获取引擎并调用其“ eval”方法即可。 您可以将数据作为字符串显式传递...

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”方法返回:

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

在Nashorn中使用Java类

如前所述,Nashorn的最强大功能之一是从JavaScript内部调用Java类。 您不仅可以访问类并创建实例,还可以对它们进行子类化,调用它们的静态成员,以及几乎可以执行的所有Java工作。

作为示例,让我们看一下线程。 JavaScript没有用于并发的任何语言功能,并且所有常见的运行时都是单线程的,或者至少没有任何共享状态。 有趣的是,在Nashorn环境中,JavaScript实际上可以在共享状态下并发运行,就像在Java中一样:

// this is how we get access to Java class Threadvar Thread = Java.type("java.lang.Thread");
// subclass with our run method var MyThread = Java.extend(Thread, {     run: function() {         print("Run in separate thread");     } }); var th = new MyThread(); th.start(); th.join(); 

请注意,从Nashorn访问类的规范方法是使用Java.type ,您可以使用Java.extend扩展类。

功能愉悦

从所有方面来看,随着JDK 8的发布,Java(至少在一定程度上)已成为一种功能 语言 。 现在,您可以在集合上使用高阶函数,例如,对它们的元素进行迭代。 高阶函数是将另一个函数作为参数并对其进行有意义的处理的函数。 看一下Java中的这个例子

List<Integer> list = Arrays.asList(3, 4, 1, 2);list.forEach(new Consumer() {
    @Override     public void accept(Object o) {         System.out.println(o);
}
}); 

在此示例中,我们不再像传统上那样使用“外部”循环遍历元素,而是将“消费者”函数传递给“ forEach”操作,该函数执行更高阶的“内部循环”操作消费者的“接受”方法,即一步一步地传递集合中的每个元素。

如前所述,用于这种高阶函数的函数语言方法宁愿接受函数参数,也不接受对象。 尽管传递对函数本身的引用在传统上不是Java的专长,但JDK 8现在具有一些语法糖,可用于仅使用lambda表达式(也称为“闭包”)来表达它们。 例如:

List<Integer> list = Arrays.asList(3, 4, 1, 2);list.forEach(el -> System.out.println(el)); 

在这种情况下,“ forEach”的参数具有这种函数引用的形式。 这是可能的,因为Consumer是一个功能接口(有时称为Single Abstract Method类型或“ SAM”)。

那么,为什么我们在Nashorn讨论中谈论lambda? 因为在JavaScript中,您也可以编写这样的代码,因此在这种情况下,Nashorn尤其准备弥合Java和JavaScript之间的鸿沟。 特别是,它甚至允许您将普通JavaScript函数作为功能接口(SAM类型)的实现来传递。

让我们看一些普通JavaScript代码,这些代码与上面的Java代码具有相同的功能。 请注意,JavaScript中没有内置列表类型,只有数组。 但是这些数组是动态调整大小的,并且具有与Java列表可比的方法。 因此,在此示例中,我们将调用JavaScript数组的“ forEach”方法:

var jsArray = [4,1,3,2];jsArray.forEach(function(el) { print(el) } ); 

相似之处显而易见。 但这还不是全部。 您还可以将这样JavaScript数组转换为Java列表:

var list = java.util.Arrays.asList(jsArray);

看到? 是的,这是Nashorn中运行JavaScript。 由于这现在是Java列表,因此可以调用其“ forEach”方法。 请注意,这与我们在JavaScript数组上调用的“ forEach”方法不同,而是在集合上定义的Java的“ forEach”方法。 尽管如此,我们还是在这里传递一个普通JavaScript函数:

list.forEach(function(el) { print(el) } );

Nashorn允许我们在需要功能接口(SAM类型)的地方提供简单JavaScript函数引用。 因此,这不仅可以通过Java实现,还可以通过JavaScript实现。

下一个版本的ECMAScript-预计将于今年最终发布-将包含一些简短的函数语法,使它们几乎可以像Java lambda一样编写,除了使用双箭头=>之外 。 这将进一步推动对齐。

特殊的Nashorn JavaScript方言

正如我在简介中提到的那样,Nashorn支持ECMAScript 5.1版本中JavaScript 以及一些扩展 。 我不一定建议使用这些扩展名,因为它们既不是Java也不是JavaScript,它们对于任何一个开发人员都会感到不自然。 另一方面,在Oracle文档中使用了两个扩展,因此我们应该熟悉它们。

首先,让我们为第一次扩展奠定基础。 如前所述,您可以使用Java.extend从JavaScript扩展Java类 如果要继承抽象Java类或实现接口,则可以使用更方便的语法。 在这种情况下,您可以虚拟地调用抽象类或接口的构造函数,并传入描述实现方法JavaScript对象文字。 JavaScript对象文字只是名称/值对,类似于您从JSON格式中可能了解的内容。 这使我们可以像下面这样实现Runnable接口:

var r = new java.lang.Runnable({    run: function() {        print("running...\n");    }});

在此示例中,我们实际上使用指定了run方法的实现的对象常量来调用Runnable的构造函数。 请注意,这是Nashorn实现为我们提供的功能,否则将无法在JavaScript中实现。

该示例的代码已经看起来类似于我们将接口实现为Java中的匿名内部类的方式,但并不完全相同。 这将我们带到第一个扩展名,该扩展名使您可以在进行构造函数调用时在结束“)”之后传递最后一个参数。 这样做,我们的代码如下所示:

var r = new java.lang.Runnable() {    run: function() {       print("running...\n");    }}; 

…完全一样,但是与Java更加相似。

第二个常用扩展名是函数的快捷方式,它使您可以在一行函数中省略花括号以及方法主体的return语句。 因此,上一节中的示例:

list.forEach(function(el) { print(el) } );

可以表示为更简洁:

list.forEach(function(el) print(el));

Avatar.js

我们已经看到,使用Nashorn,我们已经将高级JavaScript引擎嵌入到Java中。 我们还看到,从Nashorn中我们可以访问任何Java类。 Avatar.js更进一步,将“ Node编程模型,API和模块生态系统引入Java平台”。 要了解其含义以及令人兴奋的原因,我们首先必须了解Node是什么。 Node基本上会提取Chrome的V8 JavaScript引擎,使其从命令行运行而无需浏览器。 因此,它使JavaScript不仅可以在浏览器中执行,而且可以在服务器端执行。 要以任何有意义的方式在服务器上执行JavaScript,您至少需要访问文件系统和网络。 为此,Node嵌入了一个名为libuv的库,该库以异步方式执行此操作。 实际上,这意味着您对操作系统的调用永远不会阻塞,即使它们需要一段时间才能返回。 您可以提供一个回调函数,而不是阻塞函数,该函数将在调用完成后立即触发,并在有结果时传递结果。

有数家公司将Node用于严肃的应用程序,其中包括Walmart和Paypal 。

让我们看一下我从Node网站改编的一个JavaScript小示例:

// load module 'http' (this is blocking) to handle http requests
var http = require('http');// when there is a request we return 'Hello, World\n'
function handleRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, World\n');
}
// we listen on localhost, port 1337 // and give handleRequest as call back // you see the non-blocking / asynchronous nature herehttp.createServer(handleRequest).listen(1337, '127.0.0.1');
// logs to the console to reassure that we are on our way console.log('Get your hello at http://127.0.0.1:1337/ '); 

要运行此代码,您需要安装Node,然后将上面JavaScript代码保存到文件中,最后,以该文件为参数调用Node。

Avatar.js的目标是通过将libuv绑定到Java类,然后使它们可被JavaScript访问,从而提供与Node相同的核心API。 即使这听起来很麻烦,但效果却出奇的好。 Avatar.js支持大量的Node模块,对Node的主流Web框架“ express ”的支持表明它确实可以与许多现有项目一起使用。

不幸的是,在撰写本文时,Avatar.js没有二进制发行版。 有一个自述文件 ,说明了如何从源代码进行构建,但是如果您不太想从头开始构建,则也可以在不构建自己的情况下获取二进制文件 。 两种方法都行得通,但我建议第二种方法以获得更快的结果。

设置好二进制文件并将其放入lib文件夹后,您将使用以下方法调用Avatar.js框架:

java -Djava.library.path=lib -jar lib/avatar-js.jar helloWorld.js

我们假设演示服务器(上面的代码)保存在名为“ helloWorld.js”的文件中。

再次,让我们问,这为什么有用? Oracle的好人( 幻灯片10 )看到了这种库的几个用例。 我主要同意其中两个,即

  1. 您有一个Node应用程序,并且想使用某些Java库来补充Node API
  2. 您想切换到JavaScript和Node API,但需要部分或完全嵌入旧版Java代码

两种用例都可以通过使用Avatar.js并从Nashorn支持JavaScript代码中调用任何必需的Java类来工作,如我们所见。

让我给你一个第一个用例的例子。 JavaScript当前只有一种表示数字的类型,称为“数字”。 这将等同于Java的“双精度”精度,但有相同的限制。 JavaScript的数字(例如Java的double)无法表达任意范围和精度,例如在处理金钱时。

在Java中,您可以使用BigDecimal,它完全支持这一点。 但是JavaScript没有内置等效项,因此您可以从JavaScript代码访问BigDecimal类,并可以安全地处理货币值。

让我们看一个示例Web服务,它计算一定数量的百分比。 首先,我们需要一个执行实际计算的函数:

var BigDecimal = Java.type('java.math.BigDecimal');function calculatePercentage(amount, percentage) {     var result = new BigDecimal(amount).multiply(      new BigDecimal(percentage)).divide(            new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);     return result.toPlainString();
}

在JavaScript中,没有声明的类型,但除此之外,该代码看起来与我为此任务编写的Java代码非常相似:

public static String calculate(String amount, String percentage) {    BigDecimal result = new BigDecimal(amount).multiply(      new BigDecimal(percentage)).divide(           new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);     return result.toPlainString(); } 

我们只需要替换上面的Node示例的handleRequest函数即可完成我们的代码。 像这样

// load utility module 'url' to parse urlvar url = require('url');
function handleRequest(req, res) {     // '/calculate' is the path of our web service     if (url.parse(req.url).pathname === '/calculate') {         var query = url.parse(req.url, true).query;         // amount and percentage are passed in as query parameters         var result = calculatePercentage(query.amount,                                          query.percentage);         res.writeHead(200, {'Content-Type': 'text/plain'});         res.end(result + '\n');     } } 

我们使用Node的第二个核心模块来处理请求的URL,以解析出数量和百分比的查询参数。

当我启动服务器(如上所示)并发出这样的请求时

http://localhost:1337/calculate?amount=99700000000000000086958613&percentage=7.59

使用网络浏览器,我得到正确的答案“ 7567230000000000006600158.73”,而使用JavaScript的普通“数字”类型是不可能的。

当您决定将现有的JEE应用程序迁移到JavaScript和Node时,第二个用例将很有意义。 在这种情况下,您可以轻松地从JavaScript中访问所有现有服务。 另一个相关的用例是使用JavaScript和Node构建新的服务器功能,并且仍然可以从现有的JEE服务中受益。

同样的方向,还有基于Avatar.js的Project Avatar 。 详细信息不在本文讨论范围之内,但是要获得快速概述,请查看此Oracle公告 。 基本思想是用JavaScript编写应用程序并访问JEE服务。 Project Avatar带有用于Avatar.js的组合二进制发行版,但需要Glassfish进行安装和开发。

结语

Nashorn项目通过大大提高长时间运行的应用程序(例如在Web服务器内部使用)的性能,增强了原始JDK 6 Rhino的实现。 Nashorn将Java与JavaScript集成在一起,甚至考虑了JDK 8的新lambda。 真正的创新来自Avatar.js,它建立在这些功能的基础上,并提供企业Java和JavaScript代码的集成,同时与JavaScript服务器编程的事实上的标准在很大程度上兼容。

可以在Github上找到完整的示例,包括Mac OS X的Avatar.js二进制文件。

翻译自: https://www.infoq.com/articles/nashorn/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

jdk nashorn

jdk nashorn_Nashorn-JDK 8中Java和JavaScript的组合功能相关推荐

  1. 配置eclipes中java、javascript的自动代码提示

    如何配置eclipes中java.javascript的自动代码提示 Java代码: JavaScript(jsp)代码: Java代码: Java->Editor->Content As ...

  2. 在Web中如何运用JavaScript实现打印功能

    <OBJECT id=WebBrowser classid=CLSID:8856F961-340A-11D0-A96B-00C04FD705A2 height=0 width=0>< ...

  3. android java 调用js,Android中Java和JavaScript交互实例

    Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本.本文将介绍如何实现Java代码和Javascript代码的相互调用. 如 ...

  4. Nashorn——在JDK 8中融合Java与JavaScript之力--转

    原文地址:http://www.infoq.com/cn/articles/nashorn 从JDK 6开始,Java就已经捆绑了JavaScript引擎,该引擎基于Mozilla的Rhino.该特性 ...

  5. jdk中java_怎样使用JavaJDK中Java?

    什么是UDP协议 UDP协议的全称是用户数据报,在网络中它与TCP协议一样用于处理数据包.在OSI模型中,在第四层--传输层,处于IP协议的上一层.UDP有不提供数据报分组.组装和不能对数据包的排序的 ...

  6. macbook配置java环境变量_Mac系统中如何配置JDK环境变量?Mac中JDK环境变量配置教程...

    Mac系统中如何配置JDK环境变量?在使用mac系统的电脑时,如何进行设置JDK环境变量呢?接下来的文章中小编将会带来详细的介绍,希望对您有所帮助. Mac中JDK环境变量配置教程 1.访问Oracl ...

  7. Linux系统中Java环境的搭建及JDK的安装(附上每一步操作截图及说明)【一看就懂】

    开门见山 第一步: 官网下载你所要使用的jdk版本:或本地已经存在的Linux系统的jdk压缩包.(这里以jdk1.8为例) 将此压缩包按如下图所示操作(1.进入/opt/文件 2.将压缩包拖入此处) ...

  8. C++、Java、JavaScript中的异常处理(Exception)

    编程思想之异常处理 什么叫异常处理? 什么叫异常(Exception)?顾名思义就是非正常的情况,出现了不希望出现的意外,异常处理就是遇到这种意外时准备的对策和解决方案.比如您开着一辆劳斯莱斯在公路上 ...

  9. 本周推荐 | JDK 11 升级实践 和 Java 新特性浅探

    推荐语:学习java和jdk的新特性并积极应用,以达到优化系统,降本提效的作用,这是我们作为java研发同学的第一节课.本文从"为什么"起手,谈到"怎么做",最 ...

最新文章

  1. 使用Junit单元测试:Cannot instantiate test(s): java.lang.SecurityException: Prohibited package name: java
  2. Playmaker Input篇教程之引入的核心概念
  3. 中山网络推广浅析网站优化在选择关键词时需要掌握的关键性原则是什么?
  4. 多任务学习有用的资料
  5. http预请求options
  6. indexes和indices的区别(下标)
  7. 备案域名绑定服务器后 提示需要备案_小程序开发需要多少钱?
  8. QTableWidget插入项item方法 及误区
  9. SpringBoot实现的学生选课管理系统
  10. TensorFlow 实战(一)—— 交叉熵(cross entropy)的定义
  11. python中numpy.transpose()函数详解
  12. C++ 取得系统当前时间
  13. 点击某些按钮不要触发验证控件
  14. Atitit form sbmt 表单提交的几种功能方法与实现目录1.1. Atitit 表单提交 mailto协议 http协议 11.2. form-urlencoded mul
  15. 程序员在简书|努力奔跑
  16. 中职计算机办公自动化教学,中职学校计算机办公自动化教学现状分析.pdf
  17. matlab 工具箱 comsol 联,基于MATLAB与COMSOL联合仿真的电磁优化设计
  18. C语言常用转换函数实现原理
  19. 10_微信小程序-BLE低功耗蓝牙开发-连接设备
  20. Navicat使用总结(2022.9)

热门文章

  1. 【高级数据库】第一章 第02讲 DBMS概述
  2. OCS2工具包安装问题记录
  3. C 语言零基础入门教程(十一)
  4. mysql partition 实战
  5. php注册邮箱验证码,thinkphp邮箱验证码前后台
  6. CVE-2021-3560 Polkit权限提升漏洞复现与分析
  7. Linux小技巧之密码更改
  8. 浅谈模糊C均值聚类(Fuzzy C-means Clustering)
  9. HJ94 记票统计(因为数组内存未足够分配而溢出)
  10. BUG——Windows10任务栏无响应问题