使用 require.js 和 Java/Rhino 解析模块

Posted

技术标签:

【中文标题】使用 require.js 和 Java/Rhino 解析模块【英文标题】:Resolving modules using require.js and Java/Rhino 【发布时间】:2012-06-19 22:47:51 【问题描述】:

我正在尝试让 require.js 使用 Java 6 和 Rhino 在服务器端加载模块。

我能够加载 require.js 本身就好了。 Rhino 可以看到require() 函数。我可以说出来,因为当我将 require() 更改为 requireffdkj() 之类的其他名称时,Rhino 抱怨它无法找到该功能。

但是当我尝试要求一个简单的 JS 时,比如 hello.js

var hello = 'hello';

使用以下任一方法:

require('hello');
require('./hello');

它不起作用。我明白了

Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.JavaScriptException: [object Error] (<Unknown source>#31) in <Unknown source> at line number 31
    at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
    at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)

我的 hello.js 在 Java 类路径的顶部。这也是我有require.js 的地方。我尝试将hello.js 移动到任何我认为它可能会移动的地方,包括我的硬盘驱动器的根目录、我的用户目录的根目录、我运行我的 Java 应用程序的目录等等。没有任何效果。

我查看了 CommonJS 规范 (http://wiki.commonjs.org/wiki/Modules/1.0),它说*** ID(如 hello)是从“概念模块名称空间根”解析的,而相对 ID(如 ./hello)是针对调用模块解决。我不确定其中任何一个基线在哪里,我怀疑这就是问题所在。

有什么建议吗?我什至可以使用 Rhino 的 require.js 吗?

编辑:考虑到我需要按照 Pointy 在下面评论中的建议设置环境,我也尝试评估 r.js。 (我在评估require.js 之后尝试评估,然后在require.js 之前再次评估。)无论哪种情况,我都会收到错误:

Caused by: javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "arguments" is not defined. (<Unknown source>#19) in <Unknown source> at line number 19
    at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:153)
    at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:167)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:247)

“arguments”似乎是r.js 中的一个变量。我认为这是针对命令行参数的,所以我不认为r.js 是我想要做的事情的正确路径。不过不确定。

【问题讨论】:

某些东西必须提供 require.js 期望从浏览器获得的 API;也就是说,该库使用 Web 浏览器工具来加载脚本。你正在做什么来提供这些? 可能什么都没有。 :-) 我需要什么?如果你想使用 Clojure 编译器,我看到有一个 compiler.jar,但那不是我。我还看到了 rhino.jar,但它看起来像是 Rhino 本身的,Java 6 已经包含了。 好吧,我不是 100% 确定 require.js 的作用,但例如许多此类库通过在浏览器中构造新的 &lt;script&gt; 标记来获取脚本。您可能会发现简单地用 Java 编写自己的导入服务然后为您的 JavaScript 环境提供绑定会更容易;这就是我一直在做的事情(尽管从未直接使用 Rhino API;我一直使用 JSE ScriptEngine 设施)。 另外请记住,如果您编写自己的基于 Java 的脚本导入服务,它将是同步的,因此它将消除 require.js 在网络浏览器环境。 谢谢尖头。在这种情况下,我使用的是 RequireJS,因为它提供了一个 CommonJS 环境,所以我可以使用 PageDown 库,它显然希望在服务器端有一个 CommonJS 环境。 RequireJS 支持同步和异步加载,我相信上面的调用(将字符串传递给 require())而不是传递数组)是同步版本。 【参考方案1】:

require.js 适用于 rhino。最近,我在一个项目中使用它。

    你必须确保使用 r.js (not require.js) , rhino 的 require.js 的修改版本。 你必须扩展ScritableObject类来实现loadprint功能。当你调用require(["a"])时,这个类中的加载函数会被调用,你可以调整这个函数来从任何位置加载js文件。在下面的示例中,我从 classpath 加载。 您必须在共享作用域中定义属性arguments,如下示例代码所示 或者,您可以使用require.config配置子路径,以指定js文件所在的classpath中的子目录。

JsRuntimeSupport

public class JsRuntimeSupport extends ScriptableObject 

    private static final long serialVersionUID = 1L;
    private static Logger logger = Logger.getLogger(JsRuntimeSupport.class);
    private static final boolean silent = false;

    @Override
    public String getClassName() 
        return "test";
    

    public static void print(Context cx, Scriptable thisObj, Object[] args,
            Function funObj) 
      if (silent)
        return;
        for (int i = 0; i < args.length; i++)
          logger.info(Context.toString(args[i]));
    

    public static void load(Context cx, Scriptable thisObj, Object[] args,
            Function funObj) throws FileNotFoundException, IOException 
        JsRuntimeSupport shell = (JsRuntimeSupport) getTopLevelScope(thisObj);
        for (int i = 0; i < args.length; i++) 
            logger.info("Loading file " + Context.toString(args[i]));
            shell.processSource(cx, Context.toString(args[i]));
        
    

    private void processSource(Context cx, String filename)
            throws FileNotFoundException, IOException 
        cx.evaluateReader(this, new InputStreamReader(getInputStream(filename)), filename, 1, null);
    

    private InputStream getInputStream(String file) throws IOException 
        return new ClassPathResource(file).getInputStream();
    

示例代码

public class RJsDemo 

    @Test
    public void simpleRhinoTest() throws FileNotFoundException, IOException 
    Context cx = Context.enter();

    final JsRuntimeSupport browserSupport = new JsRuntimeSupport();

    final ScriptableObject sharedScope = cx.initStandardObjects(browserSupport, true);

    String[] names =  "print", "load" ;
    sharedScope.defineFunctionProperties(names, sharedScope.getClass(), ScriptableObject.DONTENUM);

    Scriptable argsObj = cx.newArray(sharedScope, new Object[] );
    sharedScope.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);

    cx.evaluateReader(sharedScope, new FileReader("./r.js"), "require", 1, null);
    cx.evaluateReader(sharedScope, new FileReader("./loader.js"), "loader", 1, null);

    Context.exit();

  


loader.js

require.config(
    baseUrl: "js/app"
);

require (["a", "b"], function(a,  b) 
    print('modules loaded');
);

js/app 目录应该在你的类路径中。

【讨论】:

谢谢sperumal。我会检查一下。现在我正在研究另一种方法(Java 7,它具有 Rhino 1.7R3,它实现了 CommonJS 模块,因此我不需要 require.js)。但我希望能够在 Java 6 中做到这一点,你的回答看起来很有帮助。会让你知道的。 这在我的 PC 上运行良好,但在 android 上失败,org.mozilla.javascript.EvaluatorExpression: Too deep recursion while parsing (r.js#1) 通过在堆栈大小较大的线程中加载r.js 来修复Android 的too deep recursion 问题。不幸的是,现在当loader.js 尝试要求一个看起来像define(value: 42); 的简单模块时,我收到以下错误:org.mozilla.javascript.EcmaError: ReferenceError: "define" is not defined. Blarg。 @sperumal 你太棒了。但是有没有办法将它与 envjs 一起使用?我没有添加对两者的支持。看起来像一个或另一个:( 我应该提一下,我在测试时确实将 seal 设置为 false

以上是关于使用 require.js 和 Java/Rhino 解析模块的主要内容,如果未能解决你的问题,请参考以下文章

关于require.js的常规使用

Require.js 与 Phonegap 和 iO 推送通知

弹出窗口 - jQuery Mobile、Backbone.js 和 Require.js

如何导入和使用方法描述在普通的js文件中使用require js?

require.js ---- 基本使用

require.js ---- 基本使用