Java 8 Nashorn JavaScript(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

Java 8 Nashorn JavaScript:连接 Java 与 JavaScript 的桥梁

前言

在 Java 8 的众多新特性中,Nashorn JavaScript 引擎是一个容易被忽视却极具潜力的工具。它如同一座桥梁,连接了 Java 与 JavaScript 两大技术生态,让开发者能够直接在 Java 环境中执行 JavaScript 代码,并实现双向交互。无论是需要为 Java 应用添加动态脚本扩展,还是希望用 JavaScript 简化 Java 的复杂操作,Nashorn 都能提供高效且直观的解决方案。本文将通过循序渐进的方式,带领读者从概念到实践,深入理解 Java 8 Nashorn JavaScript 的核心功能与应用场景。


什么是 Nashorn JavaScript 引擎?

1. Nashorn 的定位与历史

Nashorn(德语中意为“犀牛”)是 Java 8 引入的全新 JavaScript 引擎,旨在替代早期的 Rhino 引擎。它的核心目标是提供 高性能 的 JavaScript 执行能力,并与 Java 原生代码无缝集成。与 Rhino 的解释器模式不同,Nashorn 通过 即时编译(JIT) 将 JavaScript 代码转换为 JVM 字节码,从而显著提升执行速度。

类比解释
如果将 JavaScript 比作需要翻译的外语,Rhino 就像一位逐字翻译的“口译员”,而 Nashorn 则是能直接理解外语并转化为母语的“多语言者”。这种设计使 Nashorn 在处理复杂脚本时性能提升可达 10-100 倍

2. Nashorn 的核心特性

  • 与 Java 的双向交互:JavaScript 可直接调用 Java 类和方法,反之亦然。
  • 轻量级且快速:基于 JVM 的优化,无需额外依赖。
  • 兼容性:支持 ECMAScript 5.1 标准,并提供部分 ES6 特性。

环境搭建与基础配置

1. 确认 Java 版本

Nashorn 仅支持 Java 8 及以上版本。可以通过以下命令验证:

java -version

2. 使用命令行工具 jjs

Java 8 内置了 jjs 命令,可直接执行 JavaScript 脚本:

jjs -scripting hello.js

其中,-scripting 参数启用脚本模式,支持 print() 等内置函数。

3. 第一个 Nashorn 示例

创建 hello.js 文件,内容如下:

print("Hello, Nashorn!");

执行命令 jjs hello.js,控制台将输出:

Hello, Nashorn!

Java 与 JavaScript 的双向交互

1. Java 调用 JavaScript

通过 javax.script.ScriptEngine 接口,Java 可以动态执行 JavaScript 代码:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.Bindings;

public class NashornExample {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        
        try {
            // 直接执行 JavaScript 代码
            engine.eval("print('Hello from Java!');");
            
            // 调用 JavaScript 函数
            engine.eval("function add(a, b) { return a + b; }");
            Object result = engine.eval("add(3, 5)");
            System.out.println("Result: " + result);  // 输出 8
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. JavaScript 调用 Java

在 JavaScript 中,可通过 Java.type() 访问 Java 类:

// 访问 Java 的 System.out
var System = Java.type("java.lang.System");
System.out.println("Hello from JavaScript!");

// 调用 Java 的 Math 类
var Math = Java.type("java.lang.Math");
print(Math.sqrt(16));  // 输出 4.0

3. 数据类型转换

Nashorn 自动处理 Java 与 JavaScript 数据类型的转换:
| JavaScript 类型 | 对应的 Java 类型 |
|-----------------|------------------|
| Number | Double |
| String | String |
| Array | List |
| null | null |

注意:JavaScript 的 undefined 在 Java 中会被转为空对象。


进阶功能与最佳实践

1. 异常处理

JavaScript 的 try-catch 可与 Java 的异常机制结合使用:

try {
    // 故意抛出异常的 Java 方法
    var Thrower = Java.type("com.example.Thrower");
    Thrower.throwException();
} catch (e) {
    print("Caught exception: " + e.message);
}

2. 性能优化

对于重复执行的脚本,建议先编译再执行:

// 使用 ScriptEngine 的编译功能
CompiledScript compiled = engine.compile("function multiply(a, b) { return a * b; }");
compiled.eval();  // 预编译函数

3. 作用域隔离

通过 Bindings 对象管理变量作用域,避免全局污染:

Bindings bindings = engine.createBindings();
bindings.put("secretKey", "ABC123");
engine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
engine.eval("print(secretKey)", bindings);

4. 安全性限制

通过 --no-java 参数禁止 JavaScript 访问 Java API:

jjs --no-java restricted.js

实际案例:构建动态配置解析器

场景描述

假设我们需要为 Java 应用设计一个动态配置系统,允许通过 JavaScript 脚本定义配置项。

实现步骤

  1. 编写 JavaScript 配置文件 (config.js)
// 定义动态配置
function getConfig() {
    return {
        port: 8080,
        debugMode: true,
        endpoints: ["api/v1", "api/v2"]
    };
}
  1. Java 端读取配置
public class ConfigLoader {
    public static void main(String[] args) {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        
        try {
            engine.eval(new FileReader("config.js"));
            
            // 调用 JavaScript 函数并获取结果
            Object configObj = engine.eval("getConfig()");
            // 将 JavaScript 对象转为 Java Map
            Map<String, Object> config = (Map<String, Object>) configObj;
            
            System.out.println("Port: " + config.get("port"));
            System.out.println("Debug Mode: " + config.get("debugMode"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

Port: 8080  
Debug Mode: true  

结论

Java 8 Nashorn JavaScript 为开发者提供了一种灵活且高效的方式,将 JavaScript 的动态特性融入 Java 生态。无论是实现脚本化配置、构建可扩展的应用模块,还是快速验证逻辑,Nashorn 都能显著提升开发效率。尽管在 Java 11 后 Nashorn 被标记为“弃用”,但在 Java 8 至 Java 10 的版本中,它依然是连接两大语言的可靠选择。

通过本文的实践案例和代码示例,读者应已掌握 Nashorn 的核心功能与应用场景。未来,随着 JavaScript 的持续演进,Nashorn 的替代方案(如 GraalVM 的 JavaScript 引擎)可能成为新的焦点,但其设计理念与交互模式仍值得深入学习。

最新发布