Java 提供了多种方式来实现这一目标,主要分为两大类:

- 基于 Java 内置的引擎 (Nashorn):从 Java 8 开始,Nashorn 作为默认的 JavaScript 引擎被引入,它直接集成在 JDK 中,无需额外依赖,性能较好,适合在 JVM 内部执行 JS 代码。
- 基于第三方库 (如 Rhino, GraalVM):这些库提供了更强大的功能、更好的性能(特别是 GraalVM)或对特定 JS 运行时(如 Node.js)的集成。
下面我将详细介绍这两种主流方式,并提供完整的代码示例。
使用 Java 内置的 Nashorn 引擎 (推荐用于 Java 8+)
Nashorn 是一个高性能的 JavaScript 运行时,遵循 ECMAScript 5.1 规范,并支持一些 ECMAScript 6 的特性。
核心步骤
- 获取
ScriptEngine:通过ScriptEngineManager获取一个 Nashorn 引擎实例。 - 执行 JavaScript 代码:
eval(String script): 执行一段 JavaScript 字符串。eval(Reader reader): 执行一个 JavaScript 文件。put(String key, Object value): 将 Java 对象传递给 JavaScript 作用域。get(String key): 从 JavaScript 作用域获取 Java 对象。
- 数据类型转换:Nashorn 会自动处理 Java 和 JavaScript 之间的基本类型和集合类型的转换。
示例代码
基本执行
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class NashornBasicExample {
public static void main(String[] args) {
// 1. 创建 ScriptEngineManager
ScriptEngineManager engineManager = new ScriptEngineManager();
// 2. 获取 Nashorn 引擎 (名称为 "nashorn" 或 "js")
ScriptEngine engine = engineManager.getEngineByName("nashorn");
if (engine == null) {
System.out.println("Nashorn engine not found!");
return;
}
try {
// 3. 执行简单的 JavaScript 代码
System.out.println("--- 执行简单表达式 ---");
engine.eval("var x = 10;");
engine.eval("var y = 20;");
engine.eval("var sum = x + y;");
System.out.println("sum 的值是: " + engine.get("sum")); // 输出: sum 的值是: 30
// 4. 执行多行 JavaScript 代码块
System.out.println("\n--- 执行多行代码块 ---");
engine.eval("function greet(name) {\n" +
" return 'Hello, ' + name + '!';\n" +
"}");
String result = (String) engine.eval("greet('World');");
System.out.println(result); // 输出: Hello, World!
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
Java 与 JavaScript 互相传值

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.HashMap;
import java.util.Map;
public class NashornDataExchangeExample {
public static void main(String[] args) {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
try {
// --- Java 向 JavaScript 传值 ---
System.out.println("--- Java 向 JavaScript 传值 ---");
User user = new User("Alice", 30);
// 将 Java 对象 'user' 放入 JavaScript 作用域,别名为 'jsUser'
engine.put("jsUser", user);
// 在 JavaScript 中访问 Java 对象的属性和方法
engine.eval("print('从 JS 访问 Java 对象属性: ' + jsUser.name);");
engine.eval("print('从 JS 调用 Java 对象方法: ' + jsUser.getGreeting());");
// --- JavaScript 向 Java 传值 ---
System.out.println("\n--- JavaScript 向 Java 传值 ---");
// 执行 JS 代码,并获取返回值
Object result = engine.eval("var myMap = {key1: 'value1', key2: 123}; myMap;");
// JS 对象会被自动转换为 java.util.LinkedHashMap
if (result instanceof java.util.Map) {
Map<?, ?> resultMap = (Map<?, ?>) result;
System.out.println("从 JS 返回的 Map: " + resultMap);
System.out.println("key1 的值: " + resultMap.get("key1")); // 输出: value1
System.out.println("key2 的值: " + resultMap.get("key2")); // 输出: 123
}
} catch (ScriptException e) {
e.printStackTrace();
}
}
// 一个简单的 Java 类
static class User {
public String name;
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getGreeting() {
return "Hi, I'm " + this.name + " and I'm " + this.age + " years old.";
}
}
}
使用第三方库 (以 GraalVM 为例)
GraalVM 是一个现代的 JDK,它提供了一个名为 Graal.js 的高性能 JavaScript 引擎,相比 Nashorn,GraalVM 提供了更好的性能、对 ES6+ 更完整的支持,并且可以嵌入 Node.js 运行时。
环境准备
- 安装 GraalVM: 从 GraalVM 官网 下载并安装。
- 安装 JavaScript 插件: 使用 GraalVM Updater (
gu) 命令安装 JavaScript 运行时。# 在 GraalVM 的 bin 目录下执行 gu install js
- 配置项目:
- 如果你使用 Maven,在
pom.xml中添加 GraalVM 的依赖。 - 重要: 使用 GraalVM 时,需要配置 JVM 参数来启用其原生镜像功能,或者在代码中显式指定要使用的引擎。
- 如果你使用 Maven,在
Maven 依赖 (pom.xml)
<dependencies>
<!-- GraalVM JS 引擎 -->
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>23.1.0</version> <!-- 请使用最新版本 -->
</dependency>
<!-- GraalVM JS 插件,用于与 Nashorn 兼容的 API -->
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>23.1.0</version>
</dependency>
</dependencies>
示例代码
GraalVM 提供了与 Nashorn 兼容的 API,因此代码与 Nashorn 示例非常相似,主要区别在于如何获取 ScriptEngine。
import org.graalvm.js.scriptengine.GraalJSScriptEngine;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
public class GraalVMExample {
public static void main(String[] args) {
// GraalVM 建议通过 GraalJSScriptEngineProvider 来获取引擎,以确保正确初始化
// 但为了与 Nashorn 示例保持一致,我们也可以使用 ScriptEngineManager
// 注意:需要配置 JVM 参数: --module-path=path/to/graalvm/lib --add-modules=org.graalvm.js
// 或者使用 GraalVM 自带的 java
ScriptEngineManager engineManager = new ScriptEngineManager();
// GraalVM 的引擎名称也是 "js",但它会优先使用 GraalVM 的实现
ScriptEngine engine = engineManager.getEngineByName("js");
if (engine == null) {
System.err.println("GraalVM JS engine not found! Please ensure GraalVM is installed and configured.");
return;
}
try {
System.out.println("正在使用 GraalVM JS 引擎...");
// 执行 JS 文件
System.out.println("\n--- 执行 JS 文件 ---");
// 假设你有一个 project 目录下有 a.js 文件
File jsFile = new File("src/main/resources/a.js");
engine.eval(new java.io.FileReader(jsFile));
// Java 与 JS 交互
System.out.println("\n--- Java 与 JS 交互 ---");
engine.put("javaMessage", "Hello from Java!");
// 执行 JS 代码,它会调用 Java 传入的函数
engine.eval("print('JS 收到消息: ' + javaMessage);");
// 执行 JS 函数,并获取结果
engine.eval("function add(a, b) { return a + b; }");
Object sum = engine.eval("add(5, 7);");
System.out.println("JS add 函数返回结果: " + sum); // 输出: 12
} catch (ScriptException | java.io.IOException e) {
e.printStackTrace();
}
}
}
src/main/resources/a.js 文件内容:
// 这是一个简单的 JavaScript 文件
print("这是从 a.js 文件中输出的消息。");
// 定义一个函数,稍后可以被 Java 调用
function compute(a, b, operation) {
var result;
switch(operation) {
case 'multiply':
result = a * b;
break;
case 'divide':
result = a / b;
break;
default:
result = '未知操作';
}
return result;
}
// 将这个函数暴露给 Java 作用域
// 在 Java 中可以通过 engine.get("compute") 来获取这个函数
// 然后作为 Java 的 Invocable 对象来调用
通过进程调用 Node.js (适用于需要完整 Node.js 生态的场景)
如果你的 JavaScript 代码依赖于 Node.js 的特定模块(如 fs, path, axios 等),或者你已经有了一个成熟的 Node.js 服务,那么最直接的方式就是通过 Java 的 ProcessBuilder 或 Runtime 来启动一个 Node.js 进程,然后通过标准输入输出进行通信。

示例代码
准备 Node.js 脚本 (node_script.js)
// 从标准输入读取数据
process.stdin.setEncoding('utf8');
let data = '';
process.stdin.on('data', (chunk) => {
data += chunk;
});
process.stdin.on('end', () => {
try {
const input = JSON.parse(data);
const result = {
originalInput: input,
processed: input.value * 2,
message: `Processed by Node.js at ${new Date().toLocaleString()}`
};
// 将结果写入标准输出
console.log(JSON.stringify(result));
} catch (error) {
console.error(JSON.stringify({ error: error.message }));
}
});
Java 代码 (ProcessRunner.java)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
public class ProcessRunner {
public static void main(String[] args) {
// 准备传递给 Node.js 脚本的数据
Map<String, Object> inputData = new HashMap<>();
inputData.put("value", 25);
inputData.put("description", "A test number");
try {
// 1. 创建进程构建器
ProcessBuilder pb = new ProcessBuilder("node", "node_script.js");
pb.redirectErrorStream(true); // 合并错误流和输出流
// 2. 启动进程
Process process = pb.start();
// 3. 向进程的标准输入写入数据
try (OutputStream os = process.getOutputStream()) {
os.write(inputData.toString().getBytes());
os.flush();
os.close(); // 关闭输入流,表示数据发送完毕
}
// 4. 从进程的标准输出读取结果
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
}
}
// 5. 等待进程结束
int exitCode = process.waitFor();
System.out.println("Node.js 进程退出码: " + exitCode);
if (exitCode == 0) {
System.out.println("从 Node.js 收到的结果: " + output.toString());
} else {
System.err.println("Node.js 脚本执行出错: " + output.toString());
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
总结与对比
| 特性 | Nashorn (JDK 内置) | GraalVM.js | 通过进程调用 Node.js |
|---|---|---|---|
| 依赖 | 无需额外依赖 (JDK 8+) | 需要 GraalVM 和相关库 | 需要本地安装 Node.js |
| 性能 | 良好 | 非常高 | 进程间通信开销大,首次启动慢 |
| 功能 | ECMAScript 5.1 + 部分 ES6 | ES6+ / ES2025 完整支持 | 完整 Node.js 生态 |
| 集成度 | 非常高,JVM 内无缝集成 | 非常高,JVM 内无缝集成 | 低,通过外部进程和 IO 通信 |
| 适用场景 | 运行简单脚本、配置文件、业务逻辑中的动态脚本 | 对性能和 JS 版本有高要求的场景 | 必须使用 Node.js 特有模块或已有 Node.js 服务 |
| Java 8 之后 | 默认内置 | 是 Java 的未来方向,需要手动配置 | 不依赖 Java 版本 |
如何选择?
- 如果你使用 Java 8/11/17 等版本,只需要运行一些简单的 JS 逻辑:首选 Nashorn,它开箱即用,足够强大。
- 如果你追求极致性能,或者需要使用最新的 JavaScript 特性 (如
async/await,import/export):选择 GraalVM.js,它是目前最先进的 JVM JS 引擎。 - 如果你的 JS 代码严重依赖 Node.js 模块(如文件系统、网络请求库等):只能通过进程调用 Node.js,这是唯一可行的方案。
