Java动态编译源代码并加载执行

最近在看研究Hadoop时,发现官方文档中使用com.sun.tools.javac.Main编译Java源代码的,脚本如下。就研究了一番,写了个demo,记录一下,也方便后来人学习。

$ bin/hadoop com.sun.tools.javac.Main WordCount.java
$ jar cf wc.jar WordCount*.class

com.sun.tools.javac.Main 这个类位于${JAVA_HOME}/lib/tools.jar中,需要添加到classpath中或者直接在IDE中把它引入。这个方式跟直接调用javac命令效果是一样。下面是demo,使用Main类中的compile方法编译一个Person.java源文件后,再加载字节码进行执行。

1、准备待编译的java源代码。

  下面代码是一个简单的PersonAction,实现了一个行动接口Action。实现接口不是必须的,只是后面方便实例化一个有具体类型对象才用的。

import inf.Action;

public class PersonAction implements Action{

    @Override
    public void say(String msg){
        System.out.println("Person say a message: "+msg);
    }
}

package inf;

public interface Action {
    public void say(String msg);
}

2、编写执行的代码,该代码用来编译PersonAction.java,编译成功后并加载字节码到JRE中进行执行

package demo;

import inf.Action;

import java.io.*;
import java.lang.reflect.Method;

/**
 * Created by rns on 17-1-7.
 */
public class DynamicCompiler {
    public static void main(String[] args) throws IOException {
        //待编译的源代码放置的文件夹路径
        String basedir = "/home/rns/Desktop/test/";
        //待编译的类名称,不包含.java
        String classname = "PersonAction";
        //执行代码的路径,下面的路径是本人的idea编译后输出路径
        String executedir = "/home/rns/IdeaProjects_community/"
                +"DynamicCompileAndRun/out/production/DynamicCompileAndRun/";
        //创建编译器
        com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
        //设置编译命令参数,与使用javac命令后面的参数一样
        String[] params = new String[] {
                "-d",
                basedir,basedir+classname+".java",
                "-verbose"
        };
        int status = javac.compile(params);
        //当编译返回值为0时成功
        if(status == 0)
            System.out.println("compiled successfully!");
        else
            System.out.println("errors occurs");
        //部署编译好的class到执行目录
        copyTo(basedir+classname+".class",executedir+classname+".class");
        //加载class字节码并实例化,再调用相应方法
        invoke(classname,"say",new Class[]{String.class},new String[]{"Hello"});
    }

    /**
     * 实例化并调用相应方法
     * @param classname 类名
     * @param methodname 方法名
     * @param paramType 方法参数类型
     * @param paramValues 方法参数值
     */
    public static void invoke(String classname, String methodname,
                              Class[] paramType, Object[] paramValues){
        try {
            Class cls = Class.forName(classname);
            // 方式一、不转化为具体类型,
            // 利用反射创建一个Method实例,继而实现方法调用
            Method method = cls.getMethod(methodname, paramType);
            method.invoke(cls.newInstance(),paramValues);

            // 方式二、转化为具体类型(需要设计相应接口),
            // 反射实例化后强制转换为接口类型,再进行方法调用
            Action person = (Action) cls.newInstance();
            person.say(paramValues[0].toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 复制文件到指定目录
     * @param from 源文件
     * @param to 目的文件
     * @throws IOException
     */
    public static void copyTo(String from,String to) throws IOException {
        FileInputStream fi = new FileInputStream(from);
        FileOutputStream fo = new FileOutputStream(to);
        File df = new File(to);
        if(!df.exists())
            df.createNewFile();
        for(int read = fi.read(); read !=-1; read=fi.read()){
            fo.write(read);
        }
        fo.close();
        fi.close();
    }
}

 
  
3、执行结果

/usr/jdk1.8.0_111/bin/java 
...
compiled successfully!
Person say a message: Hello
Person say a message: Hello

Process finished with exit code 0


你可能感兴趣的:(Java基础)