Java 7 + Rhino 1.7R3 支持 CommonJS 模块

Java 7 + Rhino 1.7R3 support for CommonJS modules?

本文关键字:支持 CommonJS 模块 7R3 Rhino Java      更新时间:2023-09-26

我需要帮助让CommonJS在Java 7和Rhino 1.7R3上运行。

Rhino 1.7R3 支持 CommonJS 模块:

  • https://developer.mozilla.org/En/New_in_Rhino_1.7R3

Java 7 与 Rhino 1.7R3 捆绑在一起。不幸的是,Java 7的Rhino是一个修改版本,它不包括org.mozilla.javascript.commonjs包:

  • http://jdk7.java.net/rhino/README.TXT

我想通过 javax.script API 使用 Rhino 1.7R3 对 CommonJS 的支持,如下所示:

ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
engine.put("markdown", markdown);
engine.eval("var html = require('./Markdown.Sanitizer').getSanitizingConverter().makeHtml(markdown);");
return (String) engine.get("html");

(我通过ScriptEngineManager验证了我确实在使用 Rhino 1.7R3 引擎。我想也许我可以将以下依赖项添加到类路径中

<dependency>
    <groupId>org.mozilla</groupId>
    <artifactId>rhino</artifactId>
    <version>1.7R3</version>
</dependency>

CommonJS——具体来说,require()——将开始工作。但事实并非如此。当我尝试使用require()时,我得到

Caused by: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "require" is not defined. (<Unknown source>#2)
    at sun.org.mozilla.javascript.internal.ScriptRuntime.constructError(ScriptRuntime.java:3773)
    at sun.org.mozilla.javascript.internal.ScriptRuntime.constructError(ScriptRuntime.java:3751)
    at sun.org.mozilla.javascript.internal.ScriptRuntime.notFoundError(ScriptRuntime.java:3836)

如何让 Java 7 与完整版的 Rhino 1.7R3 配合使用,以便获得 CommonJS 支持?

编辑:我发现了以下问题,它涉及完全相同的主题:

健全性检查:Rhino 没有必需功能,对吧?

受访者建议,也许可以用CommonJS Rhino 1.7R3替换有限的Rhino 1.7R3,但没有说明如何做到这一点。这就是我在这里要问的。

编辑:似乎你根本不需要使用JVM引导类加载器。Sun 已将默认的 Rhino 实现类放入

sun.org.mozilla.javascript.*

包。但是你加载的 Rhino 实现会占用

org.mozilla.javascript.*

因此,它们不应该发生碰撞。但是,如果出现问题,您可以在引导类加载器的帮助下覆盖JDK中的类。您有两种选择:

基本上,你需要覆盖类路径,以便你的 Rhino 类优先而不是内置类。

  1. 只需将 rhino-1.7R3.jar 放到 your-JRE-path''lib''ext。这样,Rhino jar 将被添加到 Java Bootsrap 类加载器中,并在内置的 JavaScript jar 之前加载。
  2. 或者,如果您无权访问../lib/ext 您可以使用命令行选项:

    -Xbootclasspath/a:path/to/rhino-1.7R3.jar
    

Rhino 本身没有实现 Java 脚本 API。为了将Rhino整合到JDK中,Sun已经实现了自己的ScriptEngineScriptEngineFactory。因此,如果您加载自己的rhino-1.7R3.jar则在加载脚本时将无法使用 Common JS。

ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");

相反,您有两种选择。

  1. Rhino API 之上实现你自己的ScriptEngineScriptEngineFactory和其他相关类。了解甲骨文是如何做到的。但请注意,JDK 源代码是在 GPL 2 许可证下,所以你应该为 Rhino 发布你的自定义脚本引擎包装器,或者只使用这些类作为参考,不要复制代码。

  2. 直接使用 Rhino API我强烈推荐这种方法。Mozilla网站上有文档和示例,但基本的API相对简单:

    // Execution environment for Rhino
    // there should be only one context in a giver thread
    Context cx = Context.enter();
    // Object.prototype, Function prototype, etc.
    Scriptable scope = cx.initStandardObjects();
    // Execute script from a given java.io.Reader
    Object result = cx.evaluateReader(scope, reader, 0, null);
    // If returning result isn't sufficient for your needs
    // you can do something like this:
    Object someVar = scope.get("someVar");
    // Don't forget to close the context when you're done
    Context.exit();
    

或者,我可以为您提供一些仅限JS的解决方案。

  1. 看看浏览器化。它是一个JavaScript预处理器,它将你的源代码组合成一个捆绑包。
  2. 重写模块,使其使用 AMD 或 UMD,然后将它们与 r.js 工具组合。

这两个选项都要求您在生成过程中添加 js 预处理步骤,并且可能会使调试代码有点困难。

这个问题已经很老了,但对于那些从谷歌来到这里的人,你可以用下面的话在 Rhino 中设置 CommonJS 支持(下面只是一种方法):

(我只是直接调用 Rhino,而不是使用 Java 的脚本 API)

List<URI> paths = Arrays.asList(new URI[] { new File("js/require/paths/here").toURI() });
Context ctx = Context.enter();
Scriptable scope = ctx.initStandardObjects();
new RequireBuilder()
    .setModuleScriptProvider(new SoftCachingModuleScriptProvider(
        new UrlModuleSourceProvider(paths, null)))
    .setSandboxed(true)
    .createRequire(ctx, scope)
    .install(scope);
//
// now you can ctx.evaluateString(...) and use require()
//
Context.exit();

现在您可能有两个文件:

主.js

require('require-test.js')()

要求测试.js

module.exports = function() {
    java.lang.System.out.println('The CommonJS require function works!')
}
如果你

使用Gradle构建系统。我通过在项目 gradle 文件中添加此依赖项成功解决了这个问题。(基于这里)

compile group: 'org.mozilla', name: 'rhino', version: '1.7.10'

并且您必须将导入的包名称从 sun.org.mozilla.javascript.internal 替换为 java 类文件中的org.mozilla.javascript

// import sun.org.mozilla.javascript.internal.NativeObject; // <=== Do not use this package.
import org.mozilla.javascript.NativeObject;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

一切正常。