Java 8 Nashorn JavaScript(建议收藏)

Java 8 Nashorn JavaScript:让脚本与 Java 融合的桥梁

在 Java 8 发布之后,开发者迎来了一项极具前瞻性的功能:Nashorn JavaScript 引擎。它并非简单的“支持 JavaScript”,而是真正将 JavaScript 作为第一公民嵌入 Java 生态系统。对于初学者来说,这可能听起来像“Java 里跑 JS”这种听起来很酷但难以理解的功能;而对于中级开发者,它更像是一个隐藏的工具箱,能让你在不修改 Java 代码的前提下,灵活扩展逻辑。

本文将带你从零开始,一步步掌握 Java 8 Nashorn JavaScript 的核心用法。我们不会讲太多理论,而是通过实际代码和生活化的比喻,让你在实践中理解它的价值。


什么是 Java 8 Nashorn JavaScript?

你可以把 Java 8 Nashorn JavaScript 想象成一个“脚本翻译官”。Java 是个严谨的工程师,写代码必须提前定义类型、结构、接口。而 JavaScript 是个自由奔放的诗人,写代码可以随时改变变量类型、动态执行逻辑。

Nashorn 就是那个能同时听懂工程师和诗人的语言,并且在两者之间自由转换的翻译官。它允许你在 Java 程序中执行 JavaScript 代码,还能把 Java 对象暴露给 JS,反过来也能让 JS 函数被 Java 调用。

注意:Nashorn 从 JDK 11 开始被标记为废弃,后续版本已移除。但如果你仍在维护旧项目,或学习 Java 8 的历史特性,它依然具有学习价值。


快速入门:在 Java 中执行 JavaScript

我们先来一个最简单的例子,看看如何在 Java 代码里运行一段 JavaScript。

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class NashornExample {
    public static void main(String[] args) {
        // 1. 创建脚本引擎管理器
        ScriptEngineManager manager = new ScriptEngineManager();

        // 2. 获取 Nashorn 引擎(通过名字匹配)
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // 3. 执行一段 JavaScript 代码
        try {
            engine.eval("print('Hello from JavaScript!');");
        } catch (ScriptException e) {
            System.err.println("执行脚本时出错:" + e.getMessage());
        }
    }
}

代码注释:

  • ScriptEngineManager 是脚本引擎的“调度中心”,它能帮你找到系统中可用的引擎(如 JavaScript、Python 等)。
  • getEngineByName("nashorn") 是关键一步,它返回一个支持 JavaScript 的引擎实例。
  • eval() 方法用于执行字符串形式的 JavaScript 代码,就像你在浏览器控制台里输入代码一样。
  • print() 是 Nashorn 提供的函数,用于输出日志,相当于 Java 的 System.out.println()

运行这段代码,你会看到输出:

Hello from JavaScript!

是不是很像在写前端代码?但这一切都在 Java 程序中完成。


交互数据:Java 与 JavaScript 变量互通

Nashorn 最强大的地方在于,它不只是“运行 JS”,而是能让你在 Java 和 JS 之间传递数据。这就像两个语言在“对话”。

传递 Java 变量给 JavaScript

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class PassDataToJS {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // 1. 定义一个 Java 变量
        String name = "小明";
        int age = 25;

        // 2. 将 Java 变量绑定到 JS 环境中
        engine.put("userName", name);
        engine.put("userAge", age);

        // 3. 执行 JS 代码,并使用 Java 变量
        try {
            engine.eval("print('用户姓名:' + userName + ',年龄:' + userAge);");
        } catch (ScriptException e) {
            System.err.println("执行失败:" + e.getMessage());
        }
    }
}

代码注释:

  • engine.put("userName", name) 将 Java 变量 name 传入 JS 环境,JS 中可以通过 userName 直接访问。
  • JS 中的 + 操作符用于字符串拼接,和你平时写 JS 一模一样。
  • 输出结果为:用户姓名:小明,年龄:25

这说明 Java 和 JavaScript 的变量可以无缝对接,无需额外转换。


从 JavaScript 返回值给 Java

反过来,你也可以让 JavaScript 执行一个函数,然后把结果“传”回 Java。

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ReturnFromJS {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // 1. 定义一段 JS 函数,用于计算两个数的和
        String jsCode = "function add(a, b) { return a + b; }";

        try {
            // 2. 执行 JS 代码,定义函数
            engine.eval(jsCode);

            // 3. 调用 JS 函数,并获取返回值
            Object result = engine.invokeFunction("add", 10, 20);

            // 4. 将返回值转换为整数并打印
            System.out.println("10 + 20 = " + result);

        } catch (ScriptException | NoSuchMethodException e) {
            System.err.println("执行失败:" + e.getMessage());
        }
    }
}

代码注释:

  • engine.eval(jsCode) 执行 JS 代码,定义了一个 add 函数。
  • invokeFunction("add", 10, 20) 是关键方法,它调用 JS 中的 add 函数,并传入参数。
  • 返回值类型为 Object,因为 JS 是动态类型语言,返回值可能是数字、字符串、对象等。
  • 最终输出:10 + 20 = 30

这表明,Java 可以像调用普通方法一样调用 JS 函数,实现“跨语言调用”。


实际应用场景:动态规则引擎

想象你正在开发一个电商系统,订单金额超过一定额度需要自动审批。但审批规则可能会频繁变化,比如“满 500 减 50”、“满 1000 送积分”等。

如果每次改规则都要重新编译 Java 代码,那效率太低。这时,Nashorn 就派上用场了。

示例:动态判断是否满足优惠条件

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class DynamicRuleEngine {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // 模拟用户订单金额
        double orderAmount = 800.0;

        // 动态规则:如果订单金额大于等于 500,返回 true
        String ruleScript = 
            "function isEligible(amount) {" +
            "    return amount >= 500;" +
            "}" +
            "isEligible(" + orderAmount + ")";

        try {
            Object result = engine.eval(ruleScript);
            boolean eligible = (Boolean) result;

            if (eligible) {
                System.out.println("✅ 您的订单满足优惠条件!");
            } else {
                System.out.println("❌ 暂未达到优惠门槛。");
            }

        } catch (ScriptException e) {
            System.err.println("规则执行失败:" + e.getMessage());
        }
    }
}

代码注释:

  • 规则被写成字符串,可以来自数据库、配置文件或前端输入。
  • isEligible(amount) 函数在 JS 中定义,判断金额是否达标。
  • engine.eval(ruleScript) 执行规则逻辑,返回 truefalse
  • 最终通过 Boolean 类型强制转换,判断是否满足条件。

这种设计让你可以在不重启系统的情况下,动态更新业务规则。比如,明天规则改成“满 600 减 80”,你只需要修改字符串,无需重新编译 Java。


表格对比:Nashorn 与传统 JS 运行方式

特性 Nashorn JavaScript 浏览器 JS Node.js
运行环境 Java 虚拟机 浏览器 服务器
调用 Java 对象 ✅ 支持 ❌ 不支持 ❌ 通常不支持
动态加载规则 ✅ 非常适合 ❌ 不适合 ✅ 适合
与 Java 交互 无缝集成 无法直接交互 通过 API 交互
适用场景 Java 应用内嵌脚本 前端展示 后端服务

这个表格帮你快速判断:如果你的项目是 Java 为主,需要灵活扩展业务逻辑,Nashorn 是一个非常合适的选择。


最佳实践与注意事项

  1. 性能考虑:Nashorn 虽然比旧版 Rhino 快,但相比原生 Java 代码仍有性能差距。不要用它处理高并发计算。
  2. 安全性:运行用户输入的 JS 代码存在安全风险(如执行 eval('rm -rf /'))。务必在可信环境中使用,或加入沙箱限制。
  3. 版本兼容:Nashorn 从 JDK 11 起被废弃。如果你的项目使用 JDK 11+,建议改用 GraalVM 的 JavaScript 支持。
  4. 错误处理:始终使用 try-catch 包裹 eval()invokeFunction(),避免程序崩溃。

总结:Nashorn JavaScript 的价值

Java 8 Nashorn JavaScript 不是一个“炫技”功能,而是一个实用的桥梁。它让 Java 应用具备了“脚本化”的能力,尤其适合需要动态配置、规则灵活变更的系统。

无论是快速原型开发,还是构建可插拔的业务引擎,Nashorn 都能帮你减少硬编码,提升系统的灵活性和可维护性。

虽然它已不再被新版本 JDK 支持,但理解它,有助于你掌握“脚本与语言融合”的思想。这种思想,正是现代开发中“低代码”、“可配置化”的底层逻辑。

如果你正在维护一个 Java 8 项目,不妨尝试用 Nashorn 实现一个简单的规则引擎。你会发现,原来“让程序自己变聪明”并没有那么难。