【问题标题】:Runtime code generation and compilation运行时代码生成和编译
【发布时间】:2010-10-31 17:37:50
【问题描述】:

假设我有这段代码,它使用一些输入(例如 URL 路径)来通过反射确定要运行的方法:

// init
map.put("/users/*", "viewUser");
map.put("/users", "userIndex");

// later
String methodName = map.get(path);
Method m = Handler.class.getMethod(methodName, ...);
m.invoke(handler, ...);

这使用反射,因此可以提高性能。可以这样做:

// init
map.put("/users/*", new Runnable() { public void run() { handler.viewUser(); } });
map.put("/users", new Runnable() { public void run() { handler.userIndex(); } });

// later
Runnable action = map.get(path);
action.run();

但是像这样手动创建所有Runnables 有其自身的问题。 我想知道,我可以在运行时生成它们吗?所以我会像第一个示例一样有一个输入映射,并会动态创建第二个示例的映射。 当然,生成它只是构建一个字符串的问题,但是编译和加载它呢?

注意:我知道性能提升是如此之小,这是过早优化的完美示例。因此这是一个学术问题,我对代码的运行时生成和编译感兴趣。

【问题讨论】:

    标签: java code-generation classloader javac runtime-compilation


    【解决方案1】:

    动态生成代码的唯一方法是生成源代码并编译它,或者生成字节码并在运行时加载它。前者有模板解决方案,后者有字节码操作库。如果没有真实案例和一些分析,我认为您无法真正说出哪个会更好。从维护的角度来看,我认为反射是最好的选择。

    【讨论】:

      【解决方案2】:

      我认为您可以使用找到的代码 here 来实现这一点。前段时间我试过这个,我不确定我在哪里找到我正在使用的代码,但似乎这是一样的。

      基本上,您使用 1.6 Compiler API,但使用“非传统”方式查找​​源文件和编写类文件:Compiler 采用 Iterable<JavaFileObject>,您可以在其中插入内存支持的实现,以及JavaFileManager 处理编写类文件,您将二进制编译器输出保存在内存中。

      现在您的代码已经编译,您只需要一个自定义的ClassLoader,它可以从您的内存字节码中读取并使用正确的 FQCN 等加载类。

      而且,幸运的是,一切似乎都准备好了;)

      【讨论】:

        【解决方案3】:

        实际上,如果您一遍又一遍地调用相同的方法,反射引擎将在内部生成类似的调用存根。 (只需使用相同的 Method 对象,而不是一次又一次地重新创建它们。)

        【讨论】:

          【解决方案4】:

          好吧,您可以将代码写入 .java 文件,使用 javac (how to do that) 对其进行编译,然后使用反射将其加载到 Java 中。

          但也许,作为一种权衡,您也可以在初始化期间获取 Method 对象 - 因此您只需为每个请求调用 invoke() 方法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-07-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多