介绍 Quarkus quickjs4j:在您的 Quarkus 应用程序中实现无缝 JavaScript 集成

简介

我们很高兴地宣布 Quarkus quickjs4j 扩展的发布,这是 Quarkus 生态系统的一个强大的新成员,它可以在您的 Java 应用程序中无缝执行 JavaScript 代码。此扩展构建于 quickjs4j 库之上,将轻量级的 QuickJS JavaScript 引擎引入 JVM 和 Native Quarkus,具有完整的 CDI 集成和编译时优化。

无论您是需要执行动态业务逻辑、实现可配置的规则引擎,还是与基于 JavaScript 的算法集成,Quarkus quickjs4j 扩展都提供了一种类型安全、高性能的解决方案,可以利用 Quarkus 的构建时处理能力。

为什么在 Java 应用程序中使用 JavaScript?

Quarkus 应用程序通常需要执行动态逻辑,这些逻辑可以在不重新编译整个应用程序的情况下进行修改。JavaScript 为此用例提供了一个出色的解决方案,提供

  • 动态配置:无需重新启动应用程序即可更新业务规则和逻辑

  • 脚本功能:使高级用户能够自定义应用程序行为

  • 算法集成:利用现有的 JavaScript 库和算法

  • 快速原型设计:快速测试和迭代复杂的逻辑

Quarkus quickjs4j 扩展使这种集成无缝进行,同时保持您期望从 Quarkus 获得的性能和开发者体验。

主要特点

编译时代码生成

该扩展在构建时自动为您的 JavaScript 接口生成 CDI bean 和代理类,从而确保最佳性能和尽早发现错误。

完整的 CDI 集成

JavaScript 接口是您的 Quarkus 应用程序中的一等公民,可以像任何其他 CDI bean 一样注入。

灵活的脚本加载

从多个来源加载 JavaScript 文件:- 类路径资源(推荐用于打包的脚本)- 文件系统路径(用于动态脚本加载)- URL(用于远程脚本执行)- 其他任何地方(使用可选的 Factory 模式)

上下文支持

将 Java 对象作为上下文传递给 JavaScript 执行,从而实现 Java 和 JavaScript 代码之间的双向通信。

沙盒执行

QuickJs4J 提供了一种安全有效的方式在 Java 中执行 JavaScript。通过在沙箱中运行代码,它可以确保

  • 内存安全 – JavaScript 在隔离环境中运行,保护您的应用程序免受崩溃或内存泄漏的影响。

  • 默认情况下没有系统访问权限 – JavaScript 无法访问文件系统、网络或其他敏感资源,除非明确允许。

  • 可移植性 – 作为纯 Java 字节码,它可以在 JVM 运行的任何地方运行。

  • Native-image 友好 – 与 GraalVM 的 native-image 兼容,可实现快速、轻量级的部署。

无论您是嵌入脚本功能还是隔离不受信任的代码,QuickJs4J 都旨在实现安全无缝的集成。

入门

将扩展添加到您的 Quarkus 应用程序非常简单。首先,将依赖项添加到您的 pom.xml

<dependency>
    <groupId>io.quarkiverse.quickjs4j</groupId>
    <artifactId>quarkus-quickjs4j</artifactId>
    <version>${quarkus-quickjs4j.version}</version>
</dependency>

您还需要启用用于代码生成的注释处理器

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>io.quarkiverse.quickjs4j</groupId>
                        <artifactId>quarkus-quickjs4j</artifactId>
                        <version>${quarkus-quickjs4j.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

简单示例:JavaScript 计算器

让我们创建一个简单的计算器来演示扩展的功能。

首先,定义一个使用 @ScriptInterface@ScriptImplementation 注释的 Java 接口

package com.example;

import io.roastedroot.quickjs4j.annotations.ScriptInterface;
import io.quarkiverse.quickjs4j.annotations.ScriptImplementation;

@ScriptInterface
@ScriptImplementation(location = "calculator.js")
public interface Calculator {
    int add(int a, int b);
    int multiply(int a, int b);
}

接下来,在 src/main/resources/calculator.js 中创建 JavaScript 实现

function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

export { add, multiply };

最后,在您的 Quarkus 应用程序中注入并使用计算器

package com.example;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
public class MathService {

    @Inject
    Calculator calculator;

    public int performCalculation() {
        int sum = calculator.add(5, 3);        // Returns 8
        int product = calculator.multiply(4, 7); // Returns 28
        double quotient = calculator.divide(10.0, 2.0); // Returns 5.0

        return sum + product + (int) quotient;
    }
}

就是这样!该扩展在幕后处理 JavaScript 执行、类型转换和 CDI 集成的所有复杂性。

高级功能

用于双向通信的上下文对象

quickjs4j 的一个强大功能是能够提供 JavaScript 代码可以调用的 Java 上下文对象

@ScriptInterface(context = CalculatorContext.class)
@ScriptImplementation(location = "calculator.js")
public interface Calculator {
    int add(int a, int b);
    int multiply(int a, int b);
}
@ApplicationScoped
public class CalculatorContext {
    public void log(String message) {
        System.out.println("Calc>> " + message);
    }
}

然后,您的 JavaScript 代码可以调用这些 Java 方法

function add(a, b) {
    Calculator_Builtins.log(`Adding ${a} + ${b}`);
    return a + b;
}

function multiply(a, b) {
    Calculator_Builtins.log(`Multiplying ${a} * ${b}`);
    return a * b;
}

export { add, multiply };

用于动态脚本的工厂模式

对于需要在运行时动态加载脚本的场景,请使用工厂模式

@ApplicationScoped
public class DynamicMathService {

    @Inject
    CalculatorContext context;

    @Inject
    ScriptInterfaceFactory<Calculator, CalculatorContext> calculatorFactory;

    public void executeCustomScript() {
        // Load your javascript from some dynamic source
        String scriptContent = loadDynamicScriptContent();

        // Create calculator instance with dynamic script
        Calculator calculator = calculatorFactory.create(scriptContent, context);

        // Use the calculator
        int result = calculator.add(10, 20);
        System.out.println("Result: " + result);
    }
}

此方法非常适合需要执行用户提供的脚本或从外部源加载脚本的应用程序。请注意,脚本的执行完全在沙箱中进行。只有 Context 公开的方法可以从脚本内部调用。

错误处理和调试

JavaScript 错误作为 Java 异常传播,使调试变得简单

try {
    double result = calculator.divide(10, 0);
} catch (RuntimeException e) {
    logger.error("JavaScript execution failed: {}", e.getMessage(), e);
    // Handle the error appropriately
}

构建时魔法

在幕后,该扩展执行构建时代码生成,创建

  1. CDI Bean 类{InterfaceName}_CDI - 可注入的 CDI bean

  2. 工厂类{InterfaceName}_Factory - 可注入的工厂 bean

  3. 代理类{InterfaceName}_Proxy - 由 quickjs4j 生成

  4. 上下文内置函数{ContextName}_Builtins - JavaScript 可访问的 Java 方法

这种构建时方法确保了最小的运行时开销,同时提供了完整的 IDE 支持,包括代码完成和类型检查。

性能考虑

QuickJS 引擎专为轻量级、快速的 JavaScript 执行而设计。结合 Quarkus 的构建时优化,该扩展提供了

  • 快速启动:对应用程序启动时间的影响最小

  • 低内存占用:用于 JavaScript 执行的有效内存使用

  • Native Image 支持:与 GraalVM native image 完全兼容

  • 构建时验证:尽早检测接口不匹配和错误

用例

Quarkus quickjs4j 扩展非常适合

  • 业务规则引擎:实现可配置的业务逻辑

  • 模板处理:使用 JavaScript 模板生成动态内容

  • 算法集成:利用现有的 JavaScript 算法和库

  • 用户脚本:允许高级用户自定义应用程序行为

  • 配置逻辑:实现复杂的配置场景

当前状态和未来计划

该扩展目前处于实验状态,这意味着 API 可能会根据社区反馈而发展。我们正在积极致力于

  • 增强的错误报告和调试功能

  • 性能优化

  • 可配置的 JavaScript 引擎选项

  • 改进的 IDE 集成和工具

特别是,我们在优化性能方面还有很多工作要做。在灵活性和速度以及自定义方面,都有明显的权衡需要考虑。当前的实验性实现采用了一种非常保守的方法来确保完全沙盒化和线程安全。结果是一个较慢的实现,但它保证是线程安全且完全沙盒化的。

参与其中

Quarkus quickjs4j 扩展是 Quarkiverse 生态系统的一部分,欢迎社区贡献。无论您是否对以下内容感兴趣

  • 报告错误或请求功能

  • 贡献代码改进

  • 分享用例和示例

  • 改进文档

访问我们的 GitHub 存储库 以参与其中。我们非常希望您在 Quarkus 中试用 quickjs4j 并给我们反馈。发展功能最好的方法是听取用户的意见!

结论

Quarkus quickjs4j 扩展为需要将 JavaScript 执行集成到其应用程序中的 Java 开发者开辟了令人兴奋的可能性。凭借其编译时代码生成、完整的 CDI 集成和灵活的脚本加载选项,它为动态代码执行提供了一种强大而易于使用的解决方案。

试用一下,让我们知道您的想法!我们很高兴看到社区使用此功能构建什么。

如果您想了解更多关于 QuickJS 本身或上游 quickjs4j Java 项目的信息,这里有一些有用的链接