Java实现运行时编译并动态调用

来源:互联网 发布:excel数据透视表 编辑:程序博客网 时间:2024/06/01 10:26

Java实现运行时编译并动态调用

至于运行时编译有什么优势这里就不赘述了,直接进入主题:
JDK1.6提供了调用javac的接口。我们可以使用这个接口来实现运行时编译java文件,生成.class文件的目的。该类为javax.tools.JavaCompiler类。接下来使用自定义classloader加载,可实现动态调用。

首先,JavaCompiler使用方法如下:

    public static int compile(String name){        //获得系统的JavaComiler实例(ToolProvider在javax.javax.tools包中)        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();        //定义一个以char字节的形式输出到控制台的字节流        OutputStream out = new OutputStream() {             @Override            public void write(int b) throws IOException {                System.out.print((char)b);            }        };       //run方法第一个参数为java文件绝对路径(相对路径没试过)       //如果出错了,会将javac的信息输出到第三个参数的输出流       //如果返回值为0,说明编译成功       return compiler.run(null, null, out, name);    }

我们可以将编译好的文件使用classloader动态加载,并调用新编译的类。当然实现调用,必须要有一个公共接口。为了方便起见,我们将带编译放在该工程src/test目录下。工程目录如图所示:
这里写图片描述
其中MyTest工程中的Test文件将会在Complie运行时被Complie编译并加载
Test代码如下:

    package test;import commonface.DoSomeThing;public class Test implements DoSomeThing{    @Override    public String doSomeThing() {        return "hello world";    }}

双方的公共接口DoSomeThing:

   package commonface;public interface DoSomeThing {      public String doSomeThing();}

最后是编译类Complie:

package complie;import commonface.DoSomeThing;import javax.tools.JavaCompiler;import javax.tools.ToolProvider;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;public class Compile {    public static int compile(String name){         //注释见上文        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();        OutputStream out = new OutputStream() {             @Override            public void write(int b) throws IOException {                System.out.print((char)b);            }        };       return compiler.run(null, out, out, name);    }    public static void run(String classPath) throws Exception{         //自定义一个ClassLoader,我们需要重载findclass         ClassLoader loader=new ClassLoader(){               @Override                  protected Class<?> findClass(String className)  throws ClassNotFoundException {                    Class cLass=null;                    try {                         //根据绝对路径找到文件                         FileInputStream of=new FileInputStream(new File(className));                        //一下几步将文件流变成byte数组                        ByteArrayOutputStream out = new ByteArrayOutputStream(1000);                        byte[] b = new byte[1000];                        for (int n;(n = of.read(b)) != -1;) {                            out.write(b, 0, n);                       }                        of.close();                        byte[] bt=out.toByteArray();                        //加载类信息                        cLass= defineClass(bt, 0, bt.length);                          out.close();                          } catch (Exception e) {                          e.printStackTrace();                         throw new ClassNotFoundException();                    }                      return cLass;           }};         //加载类         Class mc=loader.loadClass(classPath);         //强制转型,并调用,结果输出在控制台         DoSomeThing dt=(DoSomeThing)mc.newInstance();         System.out.println(dt.doSomeThing());    }    public static void main(String[] args) throws Exception{          if(compile(System.getProperty("user.dir")+"\\..\\MyTest\\src\\test\\Test.java")==0){                System.out.println("Done");                //javac编译的class文件在java文件同一目录下面                run(System.getProperty("user.dir")+"\\..\\MyTest\\src\\test\\Test.class");        } else {        System.out.println("Fail");       }     }}

文中的Classloader类这里只是简单的提了一下。以后有机会再详细的说吧。另外,改程序在运行成功、如图所示:
这里写图片描述
成功的编译了Test文件,并调用了Test文件重载的doSomeThing函数,将结果输出到控制台中了。

0 0
原创粉丝点击