《一切皆是映射:代码的本质》Java 动态读取源代码,并编译 & 加载执行
动态的执行一段简单代码,采用生成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: Hello Person say a message: Hello Process finished with exit code 0

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
1、Windows下安装mysql-8.0.12及修改初始密码
1. 解压压缩包,放置到任意目录 这是我的目录:D:\Python\mysql-8.0.12-winx64\ 2. 初始化 使用命令: --initialize --console 使用CMD命令操作如下: C:\Windows\system32>D:\Python\mysql-8.0.12-winx64\bin\mysqld --initialize --console 2018-09-13T14:36:55.758742Z 0 [System] [MY-013169] [Server] D:\Python\mysql-8.0.12-winx64\bin\mysqld (mysqld 8.0.12) initializing of server in progress as process 3104 2018-09-13T14:37:12.072904Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: 4P!w2fqBruGi 2018-09-13T14:37...
- 下一篇
【译】Optaplanner开发手册本地化: (0) - 前言及概念
在此之前,针对APS写了一些理论性的文章;而对于Optaplanner也写了一些介绍性质,几少量入门级的帮助初学者走近Optaplanner。在此以后,老农将会按照Optaplanner官方的用户手册的结构,按章节地对其进行翻译,并成型一系列的操作说明文章。在文章中,为了降低对原文的理解难度,有些地方我不会直接按原文档的字面翻译,而是有可能加入一些我自己的理解,或添一些解释性的内容。毕竟英语环境下的思维和语言表达方式,跟中文或多或少会有差别的,所以如果全部按字面翻译,内容就非常生硬,可读性差,解程难度较大。我认为应该在理解了作者原意的基础上,再进一步以中文方式的表达,才算是真的的本地化。记得老农还是少农时,学习开发技术,需要阅读一些外国书箱的翻译本时,印象最深的是候捷老师的书,尽管《深入浅出MFC》,砖头厚度的书,硬是被我翻散了线,MFC尽管真的晦涩难懂,但候老却能把Windows的消息机制及MFC中整个个宏体系,系统地通俗地描述出来,令读者不需要花费太多精力去理解猜测书中字面的意义,大大降低的VC++中MFC的学习门槛。但老农毕竟只是一个一线开发人员,不是专业的技术资料翻译人才,不可...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- 2048小游戏-低调大师作品
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G