运行时代码生成和编译
假设我有这段代码,它使用一些输入(例如 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 也有其自身的问题。 我想知道,我可以在运行时生成它们吗?因此,我将像第一个示例一样有一个输入映射,并动态创建第二个示例的映射。 当然,生成它只是构建一个字符串的问题,但是编译和加载它呢?
注意:我知道性能提升很小,这是过早优化的完美例子。因此,这是一个学术问题,我对运行时生成和代码编译感兴趣。
Say I have this code that uses some input (e.g. a URL path) to determine which method to run, via reflection:
// init
map.put("/users/*", "viewUser");
map.put("/users", "userIndex");
// later
String methodName = map.get(path);
Method m = Handler.class.getMethod(methodName, ...);
m.invoke(handler, ...);
This uses reflection so the performance can be improved. It could be done like this:
// 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();
But manually creating all those Runnable
s like that has its own issues.
I'm wondering, can I generate them at runtime? So I'd have an input map as in the first example, and would dynamically create the map of the second example.
Sure, generating it is just a matter of building a string, but what about compiling and loading it?
Note: I know the performance increase is so little it's a perfect example of premature optimization. This is therefore an academic question, I'm interested in runtime generation and compilation of code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
动态生成代码的唯一方法是生成源代码并编译它,或者生成字节代码并在运行时加载它。前者有模板解决方案,后者有字节码操作库。如果没有真实的案例和一些分析,我认为你无法真正说出哪个会更好。从维护的角度来看,我认为反射是可用的最佳选择。
The only ways to generate code dynamically are to either generate source code and compile it or to generate byte code and load it at runtime. There are templating solutiions out there for the former, and bytecode manipulation libraries for the latter. Without a real case and some profiling I don't think you can really say which will be better. From a maintenance point of view I think reflection is the best option when available.
我认为您可以使用找到的代码来实现此目的 这里。不久前我尝试过这个,我不确定在哪里找到我正在使用的代码,但似乎这是相同的。
基本上,您使用 1.6 Compiler API,但使用“非传统”方式查找源文件并编写类文件:
Compiler
采用Iterable
,您可以在其中使用Iterable
。插入您的内存支持的实现,以及一个处理编写类文件的 JavaFileManager,您可以在内存中保存二进制编译器输出。现在您的代码已编译完毕,您只需要一个自定义
ClassLoader
即可从内存中的字节代码中读取并使用正确的 FQCN 等加载该类。幸运的是,所有这些似乎都已准备就绪;)
I think you can achieve this with the code found here. It is some time ago I tried this, and I'm not sure anymore where I found the code I was using, but it seems that this is the same.
Basically, you use the 1.6 Compiler API, but use an "untraditional" way to find source files and write class files: The
Compiler
takes anIterable<JavaFileObject>
, where you plug in your memory-backed implementation, and aJavaFileManager
that handles writing class files, where you hold the binary compiler output in memory.Now that your code was compiled, you only need a custom
ClassLoader
that can read from your in-memory byte code and load the class with the right FQCN etc.And, luckily, all that seems to be ready ;)
实际上,如果您一遍又一遍地调用相同的方法,反射引擎将在内部生成类似的调用存根。 (只需使用相同的
Method
对象,而不是一次又一次地重新创建它们。)Actually, the reflection engine will generate similar invocation stubs internally, if you invoke the same methods over and over again. (Just use the same
Method
objects instead of recreating them again and again.)好吧,您可以将代码写入 .java 文件,然后使用 javac 进行编译(如何做到)并使用反射将其加载到 Java 中。
但也许,作为权衡,您也可以在初始化期间获取 Method 对象 - 这样您只需为每个请求调用 invoke() 方法。
Well, you could write code to a .java file, compile it with javac (how to do that) and load it into Java using Reflection.
But maybe, as a trade-off, you could also fetch the Method objects during initialization - so you would just have to call the invoke() method for every request.