Java的动态生成编译库
从 JDK 1.6 开始java 引入了用 Java 代码重写的编译器接口,使得我们可以在运行时编译 Java 源码,然后用类加载器进行加载,让 Java 语言更具灵活性,能够完成许多高级的操作。不过,我们最期望的还是给定一段代码,直接编译,然后运行,也就是空中编译执行(on-the-fly)。
下面我用几种方法实现动态生成和执行如下面的类的方法sum():
public class Model {
private int a, b;
public Model(int a, int b) {
this.a = a;
this.b = b;
}
public int sum() {
return a + b;
}
}
本文的代码都上传到了 https://github.com/davelet/dynamic-java
javassist
这一个就不说了,直接看代码吧:https://github.com/davelet/dynamic-java/tree/master/javassist-compile
javassist 应该是我们最早接触的一个动态编译框架,它和asm(下面就是这个)都是字节码解析和生成工具。这里只说到了它的生成能力,解析能力可以自行百度。
ASM
如上面所提,也直接看代码吧:https://github.com/davelet/dynamic-java/tree/master/asm-compile
asm的生成比javassist复杂,要求我们对字节码的解释过程比较熟悉才行。所以对比javassist,asm更极客一点。
JOOR
这是github上一个开源库,用于方便的编译给定源代码的。参考代码:https://github.com/davelet/dynamic-java/tree/master/joor-lib。就是把源代码写好进行编译。
除了给定一串可编译的源码进行编译外,joor还提供了更灵活的操作能力。比如:
String world = on("java.lang.String")
.create("Hello World")
.call("substring", 6)
.call("toString")
.get();
System.out.println(world);
这段代码的意思是先创建一个字符串,再对它执行substr操作。
JavaPoet
这个不是编译库,这个是用于生成Java源代码的。生成以后可以使用jdk或者joor进行编译执行。简单参考:https://github.com/davelet/dynamic-java/tree/master/javapoet-lib
注意
在使用动态编译时,需要注意以下几点:
(1)谨慎使用
debug很困难;
(2)不要在要求高性能的项目使用
动态编译毕竟需要一个编译过程,与静态编译相比多了一个执行环节,因此在高性能项目中不要使用动态编译。
(3)动态编译要考虑安全问题
如果你在Web界面上提供了一个功能,允许上传一个Java文件然后运行,那就等于说:“我的机器没有密码,大家都来看我的隐私吧”,这是非常典型的注入漏洞,只要上传一个恶意Java程序就可以让你所有的安全工作毁于一旦。
(4)记录动态编译过程
建议记录源文件、目标文件、编译过程、执行过程等日志,不仅仅是为了诊断,还是为了安全和审计,对Java项目来说,空中编译和运行是很不让人放心的,留下这些依据可以更好地优化程序。
