运行时代码生成和编译

时间:2010-10-31 17:37:50

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

假设我有这个代码使用一些输入(例如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();

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

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

4 个答案:

答案 0 :(得分:3)

动态生成代码的唯一方法是生成源代码并对其进行编译或生成字节代码并在运行时加载它。前者有模板解决方案,后者有字节码操作库。没有真实案例和一些分析我不认为你真的可以说哪个更好。从维护的角度来看,我认为反思是可行的最佳选择。

答案 1 :(得分:2)

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

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

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

幸运的是,所有这些似乎已经准备就绪;)

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

但是,作为权衡,你也可以在初始化期间获取Method对象 - 所以你只需要为每个请求调用invoke()方法。