
《一切皆是映射:代码的本质》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
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2025年03月20日 14时50分37秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
计时器模仿地球绕太阳圆周运动
2019-03-01
1144. The Missing Number (20)
2019-03-01
为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接
2019-03-01
tp5.1 页面错误!请稍后再试~ 安装好后,提示错误
2019-03-01
删除外键约束
2019-03-01
c++ 预处理命令 #error 用法
2019-03-01
Qt Creator编码
2019-03-01
Linux部署sendmail邮件服务器
2019-03-01
【今日CV 计算机视觉论文速览 第97期】Tue, 9 Apr 2019
2019-03-01
庄子:谁知南华秋水意?
2019-03-01
Thread.sleep() 和 Thread.yield() 区别
2019-03-01
第1讲 快速入门 《Kotlin 极简教程 》
2019-03-01
官宣:湘江智能“车-站-路-云”一体化协同智慧公交解决方案来啦!
2019-03-01
[OpenGL ES] VBO 顶点缓冲对象
2019-03-01
UltraEdit不产生bak 文件可能不是DOS格式
2019-03-01
云计算-大数据-云安全高等教育改革示范教材
2019-03-01
Web站点安全监控
2019-03-01
使用MaxCompute进行数据质量核查
2019-03-01
JavaScript 自学手册(文档教程)
2019-03-01
Java语言特点与学习
2019-03-01