这是2024年初的 GraalVM 系列博文,当时写了大纲,知道一年半后的现在才得以完成发布😄

1、概述

实话说,标题的场景为小众需求,日常开发基本用不到,我是最近在做一个低代码轮子玩具 app-meta 需要实现 FaaS(Function as a Service)功能才接触到 JS 引擎。还有如下的场景会用的上:

  • 调用 js 特有的函数(java 体系没有更好的选择)
  • 动态执行代码(代码逻辑随时可修改,这一块脚本语言有天然优势)
  • 需要利用脚本语言扩展 Java 功能

我计划针对在JVM跑JavaScript脚本写系列的文章:

  1. 在JVM跑JavaScript脚本 | Oracle GraalJS 简介与实践
  2. 在JVM跑JavaScript脚本 | FaaS架构简单实现

温馨提示:文章内容较长,可按需定位章节阅读😃

1.1、JVM 下 JS 引擎

内置引擎

引擎所属 JDK 版本基于的 ECMAScript 版本备注
RhinoJDK 6 及之前(Java 1.6)ES3(部分 ES5)由 Mozilla 用 Java 编写,最早的 JVM JS 引擎,速度慢但易集成。
NashornJDK 8 ~ JDK 14ES5.1(少量 ES6 特性)Oracle 开发,性能较 Rhino 高,支持 Java <-> JS 互操作;JDK 15 开始移除。

外部高性能引擎

引擎运行机制特点项目链接
Graal.jsGraalVM 提供的 JS 实现支持 ES2022 及后续,性能高,可与 Java 混合调用,无需 JNI 手写Graal.js 官方
JavetJNI 调用 V8 引擎完整支持现代 JS/Node API,性能接近 Node.jsJavet
Duktape-Java嵌入 Duktape 引擎小巧、易嵌入、启动快,适合轻量脚本执行Duktape-Java
QuickJS-JavaJNI 调用 QuickJS支持最新 JS 特性(ES2020+),内存占用小QuickJS-Java

再后来,GraalVM 横空出世,它是 Oracle Labs 开发的一款 高性能、多语言虚拟机,目标是在 同一个运行时 下高效运行多种编程语言(Java、JavaScript、Python、Ruby、R、LLVM-based 语言、WebAssembly 等),并且实现这些语言之间的无缝互操作。

主要组件

组件作用
Graal Compiler高性能 JIT 编译器,可替代 HotSpot 的 C2 编译器。
GraalJS在 GraalVM 上运行的 JavaScript/Node.js 实现,支持现代 ECMAScript 规范。
Truffle一套多语言实现框架,用于开发新语言运行时。
Native ImageAOT 编译工具,将 Java 应用打包成本地二进制可执行文件。
Polyglot API提供跨语言调用的统一 API。

今天我们的主角就是 GraalJS。

1.2、 GraalJS 简介

GraalJS: A ECMAScript 2022 compliant JavaScript implementation built on GraalVM. With polyglot language interoperability support. Running Node.js applications!

翻译过来就是,GraalJS 是基于 GraalVM 构建,兼容 ECMAScript 2022 语法的 JavaScript 实现,能够运行 Node.js 应用,同时支持 polyglot (多语言互操作)。

为什么选择它?

最主要原因是它支持较新的 js 语法,有大公司背书,还考虑到 GraalVM 还支持其他脚本语言(如 python),有利于以后的功能扩展。


2、开始使用

📦 依赖引入

此处以 maven 为例

<!-- 增加 GraalJS 依赖,graalvm.version 替换为最新的版本号即可 -->
<properties><graal.version>24.2.1</graal.version>
</properties><dependencies><dependency><groupId>org.graalvm.polyglot</groupId><artifactId>polyglot</artifactId><version>${graal.version}</version></dependency><dependency><groupId>org.graalvm.polyglot</groupId><artifactId>js</artifactId><version>${graal.version}</version><type>pom</type></dependency>
</dependencies>

👋 惯例 Hello World

import org.graalvm.polyglot.Context;public class GraalJSDemo {public static void main(String[] args) {try (Context context = Context.create()) {context.eval("js", "console.log(`来自 GraalJS 的问候!Time=${Date.now()}`)");}}
}

代码浅析

  • ContextGraalVM Polyglot API 的核心类。它代表一个“多语言执行上下文”(Execution Context),里面可以执行不同语言的代码,比如 "js"(JavaScript)、"python""ruby" 等。

  • 每个 Context 可以看成是一个沙箱(sandbox),里面有独立的全局变量、函数等运行环境。

  • 使用 try-with-resources,保证 Context 在使用结束后会自动关闭并释放资源(例如内存、线程等)。

  • Context.create() 会创建一个默认的多语言上下文:

    • 默认启用 JavaScript、Python 等 GraalVM 已安装的语言(如果你是 GraalVM Standard Edition,可能默认只开启 JavaScript)。
    • 你也可以用 Context.create("js") 来只创建 JS 运行环境(更精简)。
  • eval(languageId, sourceCode) 用来在指定语言中执行一段代码。

    • languageId"js" 代表执行 JavaScript 代码。
    • sourceCode"console.log('Hello from GraalJS!')" 是要运行的 JavaScript 源码。
  • 在 GraalVM 里,console.log 是 Graal.js 提供的一个 JS 全局函数,输出到 Java 的标准输出(System.out)。

执行后,你会在 Java 控制台看到:

💱 参数传递

我们可以在 JavaScript 里定义函数,然后从 Java 调用它,传递参数。这种方式适合当脚本是函数而不是全局执行代码

/*** 构建一个 JS 函数,支持传递参数并得到结果*/
@Test
public void funWithParams(){try(Context ctx = Context.create(JS)){// 构建函数对象Value addFunc = ctx.eval(JS, "(x, y)=> x+y");// 传递参数调用它int result = addFunc.execute(100, 100).asInt();System.out.println("执行 100+100 函数,结果="+result);}
}

🔌 全局变量

全局变量就是给 JS 引擎赋予全局可访问的值,类似于 HTML 中的 window😄。这里就需要用到Bindings组件。GraalVM 的 Bindings 类似于一个共享的变量表,你可以在 Java 里放值,JS 直接读取。同时参数类型也会自动映射(Java 数字 → JS 数字)👍。

定义 Java 类

public class JavaLogger {// 定义时间格式器(HH表示24小时制,hh表示12小时制)DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");private void log(String level, String msg){String time = LocalTime.now().format(formatter);System.out.printf("[JAVA] %s %-5s %s%n", time, level, msg);}public void info(String msg){ log("INFO", msg); }public void debug(String msg){ log("DEBUG", msg); }public void error(String msg){ log("ERROR", msg); }
}
private void printValue(Value value){System.out.printf("%n------------------------ 脚本返回值 ------------------------%n%s", value);
}/*** 设置全局变量*/
@Test
public void bindings(){/*** 自定义 Java 、JS 互通规则,按需开启对应的权限-*/HostAccess hostAccess = HostAccess.newBuilder()//允许不受限制地访问所有公共构造函数、公共类的方法或字段.allowPublicAccess(true)//允许客户端语言实现任何 Java 接口.allowAllImplementations(false)//允许客户端语言实现(扩展)任何 Java 类.allowAllClassImplementations(false)//允许访问数组.allowArrayAccess(false)//允许访问 List.allowListAccess(false)//允许客户应用程序以缓冲区元素的形式访问 ByteBuffers.allowBufferAccess(false)//允许客户应用程序使用迭代器将可迭代对象作为值进行访问.allowIterableAccess(false)//允许客户应用程序将迭代器作为迭代器值进行访问。.allowIteratorAccess(false)//允许客户应用程序以哈希值形式访问 Map 对象.allowMapAccess(true)//允许客户应用程序继承对允许方法的访问权限.allowAccessInheritance(false).build();// 使用自定义 HostAccess 构建 Contexttry(Context ctx=Context.newBuilder(JS).allowHostAccess(hostAccess).build()){Value global =  ctx.getBindings(JS);global.putMember("UUID", UUID.randomUUID().toString());// 传递 Map 键值对global.putMember("User",Map.of("name", "集成显卡","url", "https://github.com/0604hx"));// 放置对象示例global.putMember("log", new JavaLogger());String script = """log.info(`开始执行 JS 脚本,UUID=${UUID}`)log.debug(`测试 debug 日志...`)log.error(`测试 error 日志...`)let result = { time: Date.now(), name: User.name, uuid: UUID }result""";printValue(ctx.eval(JS, script));}
}


关于 HostAccess 权限,可以查看官方文档:HostAccess.Builder。

⛑️ 安全管理

allowAllAccess

Context.allowAllAccess 是 GraalVM Polyglot API 里 Context.newBuilder() 的一个配置,用来放开 Java 与其他语言之间的所有访问限制。

如果通过context.allowAllAccess(true),则表示:“我信任这个脚本,允许它干任何事,包括直接操作 Java 类、方法、字段,甚至文件系统和网络”。对于不明来源不明作用的脚本,这是非常危险的!所以,该项是默认 false。除非特殊情况,我都强烈建议关闭它。在脚本真要调用什么 Java 代码,可以通过全局对象来实现。

开启 allowAllAccess(true) 后:

  • 解除几乎所有安全限制
  • JS / Python / 其他脚本语言可以直接调用 Java API
  • 可以访问文件、网络、系统属性等

例子(JS 调用 Java 类):

try (Context context = Context.newBuilder("js").allowAllAccess(true).build()) {context.eval("js", """const File = Java.type('java.io.File');let f = new File('test.txt');console.log("Absolute Path:", f.getAbsolutePath());""");
}

如果没有 allowAllAccess(true),上面会抛异常:

java.lang.SecurityException: Access to host classes is not allowed.

allowIO

默认情况下, Context 是不允许执行 I/O 操作(输入输出)的,包括读写文件、访问标准输入输出流、打开网络连接等。必要情况可通过context.allowIO(IOAccess.ALL)开启。

附录

源代码

本文所有源代码均在:⭐Java实用示例合集-GraalJS ⭐

参考资料

  • 全栈虚拟机GraalVM初体验
  • clever-graaljs:基于 graaljs 的高性能js脚本引擎,适合各种需要及时修改代码且立即生效的场景,如:ETL工具、动态定时任务、接口平台、工作流执行逻辑。 fast-api 就是基于clever-graaljs开发的接口平台,可以直接写js脚本开发Http接口,简单快速!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95741.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95741.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95741.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 EC 数据与大模型技术实现天气预报:从数据到上线的全栈方法

1. 先校准“EC 数据”与“AI 预报”的语境 EC 数据家族(常用) IFS/HRES:确定性全球模式,水平分辨率约 9 km,常用预报范围 10 天; IFS/ENS:51 成员集合预报,提供 15 天概率信息; ERA5:再分析数据,小时级、0.25,可追溯至 1940 年,用作训练/评测黄金基准。 AI 预报…

迭代器模式及优化

迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;用于提供一种统一的方式遍历聚合对象&#xff08;如集合、容器&#xff09;中的元素&#xff0c;而无需暴露对象的内部实现细节。它将遍历逻辑与聚合对象分离&#xff0c;使得遍历操作可以…

纯Qt手撕gb28181协议/gb28181协议服务端/gb28181协议设备端/gb28181设备模拟器/gb28181虚拟监控设备

一、前言说明 搞完onvif设备模拟器&#xff0c;总想着把28181设备模拟也实现&#xff0c;因为之前已经花了大力气把28181平台软件端实现了&#xff0c;为了实现这个组件&#xff0c;头发掉了一大把&#xff0c;专门把国标文档看了好几遍&#xff0c;逐行阅读&#xff0c;针对需…

【渗透实战】无下载器环境(curl/wget)下玩转 Metasploit 自动利用

1. 背景与问题场景 在渗透测试或漏洞利用中&#xff0c;Metasploit&#xff08;MSF&#xff09;是业界最常用的框架之一。 其许多 RCE&#xff08;远程代码执行&#xff09;模块在落地 payload&#xff08;如 Meterpreter 或反弹 shell&#xff09;时&#xff0c;采用了 CMD St…

jd-hotkey探测热点key

对任意突发性的无法预先感知的热点数据&#xff0c;包括并不限于热点数据&#xff08;如突发大量请求同一个商品&#xff09;、热用户&#xff08;如恶意爬虫刷子&#xff09;、热接口&#xff08;突发海量请求同一个接口&#xff09;等&#xff0c;进行毫秒级精准探测到。然后…

C#WPF实战出真汁07--【系统设置】--菜品类型设置

1、菜品设置介绍 菜品设置跟餐桌设置的功能目的是相同的&#xff0c;包括了新增&#xff0c;删除&#xff0c;编辑&#xff0c;分页&#xff0c;查询&#xff0c;重置&#xff0c;全选&#xff0c;全消&#xff0c;列表功能&#xff0c;实现流程也是布局设计&#xff0c;后台逻…

aave v3 存款与借款利息的计算方式

本文只涉及到利率计算的数学原理&#xff0c;不作源码解析:存款首先我们假设小明在aave里面存了10000usdt&#xff0c;存的时候年化收益率是5%,那么半年后其存款的利息是多少呢?常规的计算方式如下:利息10000*5%*(存款的时长/一年的时长)这么做有什么问题呢&#xff1f;假设现…

Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析

&#x1f4cb; 目录 项目概述 技术架构深度解析 核心功能模块详解 代码实现分析 使用场景与实战案例 性能优化与最佳实践 扩展开发指南 总结与展望 项目概述 什么是Windows-MCP.Net&#xff1f; Windows MCP.Net是一个基于.NET 10.0开发的Windows桌面自动化MCP&…

Boost.Asio学习(7):Boost.Beast实现简易http服务器

namespace beast boost::beast;beast::flat_buffer是一个用于 Boost.Asio 和 Boost.Beast 网络读写的缓冲区实现。专为 一次性顺序读取 / 消费 场景设计&#xff0c;比 std::string 或 std::vector 高效&#xff0c;因为它是扁平内存结构&#xff08;contiguous memory&#x…

深入解析JVM内存区域划分:从理论到实践

Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心环境&#xff0c;它负责管理内存分配、垃圾回收、字节码执行等关键任务。理解JVM的内存区域划分&#xff0c;对于优化Java应用性能、排查内存问题&#xff08;如OutOfMemoryError、StackOverflowError&#xff09;至…

滑窗|贪心|✅滚动数组

lc17.08pair按身高升序、相同时体重降序排序结果是找体重序列的最长递增子序列长度核心&#xff1a;转化为二维最长递增子序列问题求解vector<int> dp;for (auto& p : hw) {int w p.second;auto it lower_bound(dp.begin(), dp.end(), w);if (it dp.end()) {dp.pu…

深入理解数据库架构:从原理到实践的完整指南

一、数据库存储架构的多维度分类体系 1.1 基于数据组织方式的存储架构分类 数据库的存储架构从根本上决定了其性能特征、适用场景和扩展能力。理解不同的数据组织方式是选择合适数据库技术的基础&#xff0c;这种分类不仅反映了技术实现的差异&#xff0c;更体现了对不同业务需…

体彩排列三第2025218期号码分析

大家好&#xff0c;本人蔡楚门来此平台分享一下本期得经验和思路&#xff0c;希望能够给大家带来好的运气和灵感&#xff01;体彩排列三第2025218期号码分析&#xff0c;大小号码数字分析&#xff0c;上期开出全小号码最多&#xff0c;最近两期的开奖号码全部都是全小号码最多&…

java设计模式之迪米特法则介绍与说明

一、核心概念与目标 基本定义 迪米特法则的核心思想是&#xff1a;一个对象应该对其他对象尽可能少地了解&#xff0c;仅与直接关联的对象&#xff08;即“朋友”&#xff09;通信&#xff0c;避免与“陌生人”产生直接交互。 直接朋友&#xff1a;包括当前对象的成员变量、方法…

2024-2025华为ICT大赛中国区 实践赛昇腾AI赛道(高职组)全国总决赛 理论部分真题+解析

Part 1 昇腾AI全栈系统模块(共6题)&#xff1a;1、许多计算芯片可以设计作为人工智能的计算芯片&#xff0c;但不同的芯片计算性能不同&#xff0c;昇腾计算芯片是一种()芯片。(单选题)A.CPU B.GPU C. NPU D.TPU正确答案&#xff1a;C解析&#xff1a;A项CPU中央处理器的架…

网络安全和基础设施安全局 (CISA) 表示微分段不再是可选的

网络安全和基础设施安全局 (CISA) 最近发布了一系列指导文件中的第一份&#xff0c;旨在帮助联邦机构实施微分段&#xff0c;作为其零信任架构 (ZTA) 战略的一部分&#xff0c;以遵守2022 年白宫的授权。 该文件《零信任中的微分段&#xff0c;第一部分&#xff1a;介绍和规划…

Spring Boot SseEmitter 重复请求问题深度分析与解决方案

1. 前言 在使用 Spring Boot 开发流式接口(Server-Sent Events)时,我们遇到了一个令人困惑的问题:每次 SseEmitter 完成后,都会触发第二次请求,导致重复请求检测机制误报。本文将详细记录问题的发现、分析过程以及最终的解决方案。 2. 系统架构背景 2.1 请求处理架构 …

心路历程-三个了解敲开linux的大门

学习前都爱唠叨一番&#xff1a; 了解一下&#xff1a;互联网的发展是离不开服务器的&#xff0c;而服务器的系统主流的还是Linux&#xff1b;这个是有数据进行支撑的&#xff1b;这个只是作为了解而已&#xff0c;我们并不买课&#xff0c;也不做什么买卖的行为&#xff0c;仅…

关于“双指针法“的总结

笔者这些天终于达成了只狼的全成就&#xff0c;甚是欢喜。然而乐极生悲&#xff0c;最近做了些算法题&#xff0c;竟没有一道靠自己做出来。感觉算法题常常用到“双指针法”呢……为什么到现在我还是做不出来这些算法题……今天就来试着总结一下它的使用场景吧。快慢指针法又名…

基于51单片机的智能吊灯

基于 51 单片机的智能吊灯设计与实现论文简纲一、引言1.1 研究背景与意义阐述传统照明设备在节能性、智能化方面的不足&#xff0c;结合智能家居产业发展趋势&#xff0c;说明设计基于 51 单片机的智能吊灯对提升生活便利性、降低能耗的现实意义。1.2 国内外研究现状简要介绍当…