Java动态编译

来源:互联网 发布:mac虚拟机内存分配 编辑:程序博客网 时间:2024/05/16 17:52
动态编译一直是Java的梦想,从Java1.6开始支持动态编译,可以在运行期直接编译.java文件,执行.class,并且能够获取相关的输入输出,甚至还能监听相关事件。
给出一个简单的动态编译例子:
DynamicCompilation.javapackage org.xxz.test;import java.io.IOException;import java.lang.reflect.Method;import java.net.URI;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import javax.tools.JavaCompiler;import javax.tools.JavaFileObject;import javax.tools.SimpleJavaFileObject;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;public class DynamicCompilation {    public static void main(String[] args) throws Exception {        // java源代码        String sourceStr = "public class Hello{" + //                "public String sayHello(String name){return \"Hello,\" + name + \"!\";}" + //                "}";        // 类名以及文件名        String clsName = "Hello";        // 方法名        String methodName = "sayHello";        // 当前编译器 手动的将jdk/lib/tool.jar 复制到 jre/lib/目录下,不然会有空指针异常        JavaCompiler cmp = ToolProvider.getSystemJavaCompiler();        // Java标准文件管理器        StandardJavaFileManager fm = cmp.getStandardFileManager(null, null, null);        // Java文件对象        JavaFileObject jfo = new StringJavaObject(clsName, sourceStr);        // 编译参数,类似于javac <options>中的options        List<String> optionsList = new ArrayList<String>();        // 编译文件的存放地方,注意:此处是为Eclipse工具特设的        optionsList.addAll(Arrays.asList("-d", "./bin"));        // 要编译的单元        List<JavaFileObject> jfos = Arrays.asList(jfo);        // 设置编译环境        JavaCompiler.CompilationTask task = cmp.getTask(null, fm, null, optionsList, null, jfos);        // 编译成功        if (task.call()) {            Object obj = Class.forName(clsName).newInstance();            Class<? extends Object> cls = obj.getClass();            // 调用sayHello方法            Method method = cls.getMethod(methodName, String.class);            String str = (String) method.invoke(obj, "Dynamic Compilation");            System.out.println(str);        }    }}class StringJavaObject extends SimpleJavaFileObject {    private String content = "";    // 遵循Java规范的类名及文件    public StringJavaObject(String _javaFileName, String _content) {        super(_createStringJavaObjectUri(_javaFileName), Kind.SOURCE);        content = _content;    }    // 产生一个URL资源路径    private static URI _createStringJavaObjectUri(String name) {        return URI.create("String:///" + name + Kind.SOURCE.extension);    }    // 文本文件代码    @Override    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {        return content;    }}

动态编译虽然很好,但是我们要注意以下几点:
    1,在框架中谨慎使用
    比如在Struts中使用动态编译,动态实现一个类,它若继承ActionSupport就希望它成为一个Action。能做到,但是Debug很困难;在比如在Sping中,写一个动态类,要让他动态注入到Spring容器中,这是需要花费老大功夫的。
    2,不要在高性能的项目中使用
    动态编译毕竟需要一个编译过程,与静态编译比多了一个执行环节,因此在高性能项目中不要使用动态编译。不过,如果是在工具类项目中它则可以很好的发挥其优越性,比如在Eclipse工具中写一个插件,就可以很好的使用动态编译,不用重启就可以实现运行,调试功能,非常方便。
    3,动态编译要考虑安全问题
    如果你在WEB界面上提供一个功能,允许上传一个Java文件然后运行,这是典型的注入漏洞,只要上传一个恶意的Java程序,就可以让你所有的安全工作毁于一旦

    4,记录动态编译过程
    建议记录源文件,目标文件,编译过程,执行过程等日志,不仅仅是为了诊断,还是为了安全和审计,对Java项目来说,空中编译和运行时很不让人放心的,留下这些依据可以更好的优化程序。