2010-10-31 124 views
3

说我有这个代码,使用一些输入(例如,URL路径),以确定要运行的方法,通过反射:运行时代码生成和编译

// init 
map.put("https://stackoverflow.com/users/*", "viewUser"); 
map.put("/users", "userIndex"); 

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

此使用反射所以性能可以得到改善。它可以这样做:

// init 
map.put("https://stackoverflow.com/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 s就像它有它自己的问题。 我想知道,我可以在运行时生成它们吗?所以我会像第一个例子那样有一个输入映射,并且会动态地创建第二个例子的映射。 当然,生成它只是建立一个字符串的问题,但编译和加载它呢?

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

回答

3

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

0

那么,您可以编写代码到.java文件,使用javac编译它(how to do that)并使用Reflection将其加载到Java中。

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

2

我想你可以通过代码here来实现这一点。就在前一段时间,我尝试了这一点,而且我不知道在哪里找到了我正在使用的代码,但似乎这是相同的。

基本上,您使用1.6编译器API,而是使用“非传统”的方式来查找源文件和编写类文件:Compiler需要一个Iterable<JavaFileObject>,你在你的记忆支持的实施插上,和JavaFileManager该手柄编写类文件,在那里你将二进制编译器输出保存在内存中。

现在,您的代码编译,你只需要一个定制ClassLoader可以从你的内存字节码阅读并正确FQCN等

而且,幸运的是,所有这似乎是加载类准备好;)

1

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