《一切皆是映射:代码的本质》Java 动态读取源代码,并编译 & 加载执行
发布日期:2021-05-04 16:28:31 浏览次数:22 分类:技术文章

本文共 5170 字,大约阅读时间需要 17 分钟。

动态的执行一段简单代码,采用生成java文件,调用javac编译,反射执行的方式。

使用输入输出流(或者你说的可能是要用反射得到程序结果来解析)解析做出*.Java文件。

然后可以使用runtime调用Dos下的java编译命令编译取得class文件。
然后使用classloader,反射等组合执行生成的class文件。

package loadjarclass; import java.io.File;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader; import org.junit.Test;  public class LoadJarClassTest {        @Test    public void testLoadClass() throws Exception{        /*动态加载指定类*/        File file=new File("D:/test");//类路径(包文件上一层)        URL url=file.toURI().toURL();        ClassLoader loader=new URLClassLoader(new URL[]{url});//创建类加载器        //import com.sun.org.apache.bcel.internal.util.ClassLoader;        //ClassLoader classLoader = new ClassLoader(new String[]{""});//类路径        Class
cls=loader.loadClass("loadjarclass.TestTest");//加载指定类,注意一定要带上类的包名 Object obj=cls.newInstance();//初始化一个实例 Method method=cls.getMethod("printString",String.class,String.class);//方法名和对应的参数类型 Object o=method.invoke(obj,"chen","leixing");//调用得到的上边的方法method System.out.println(String.valueOf(o));//输出"chenleixing" /*动态加载指定jar包调用其中某个类的方法*/ file=new File("D:/test/commons-lang3.jar");//jar包的路径 url=file.toURI().toURL(); loader=new URLClassLoader(new URL[]{url});//创建类加载器 cls=loader.loadClass("org.apache.commons.lang3.StringUtils");//加载指定类,注意一定要带上类的包名 method=cls.getMethod("center",String.class,int.class,String.class);//方法名和对应的各个参数的类型 o=method.invoke(null,"chen",Integer.valueOf(10),"0");//调用得到的上边的方法method(静态方法,第一个参数可以为null) System.out.println(String.valueOf(o));//输出"000chen000","chen"字符串两边各加3个"0"字符串 } }

使用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: HelloPerson say a message: Hello Process finished with exit code 0
上一篇:图书推荐:Kotlin从入门到进阶实战
下一篇:《一切皆是映射:代码的本质》哈希算法 (Hash)

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年03月20日 14时50分37秒