1 前言

Java的动态编译知识,真真在实际开发中并不是经常遇到。但是学习java动态编译有助于我们从更深一层次去了解java。对掌握jdk的动态代理模式,这样我们在学习其他一些开源框架的时候就能够知其然也知其所以然。下面我们来使用java的动态编译。有关java动态编译的API都在javax.tools包中

2 使用JavaCompiler接口来编译java源程序

 

使用 Java API来编译 Java源程序有非常多方法,目前让我们来看一种最简单的方法,通过JavaCompiler进行编译。我们能通过ToolProvider类的静态方法getSystemJavaCompiler来得到一个JavaCompiler接口的实例。

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
   JavaCompiler中最核心的方法是run。通过这个方法能编译 java源程序。这个方法有 3个固定参数和1个可变参数(可变参数是从Jave SE5开始提供的一个新的参数类型,用type… argu表示)。前3个参数分别用来为 java编译器提供参数、得到 Java编译器的输出信息及接收编译器的错误信息,后面的可变参数能传入一个或多个Java源程式文件。如果run编译成功,返回0。

int run(InputStream in, OutputStream out, OutputStream err, String... arguments)

如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和 System.err。如果我们要编译一个 hello.java文件,并将使用标准输入输出,run的使用方法如下:

int results = tool.run(null, null, null, "Hello.java");

完整的代码如下(用的是Eclipse工具)

2.1 Eclipse工程截图

2.2  Hello.java

   首先写好这个java文件,用来演示java动态编译Hello.java文件
package my.xyz.hello;

public class Hello {

	public static void main(String[] args) {
		System.out.println("helloworld!");
	}
}

2.3 DynamicCompileTest.java

package my.xyz.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class DynamicCompileTest {
	public static void main(String[] args) throws Exception {
		// 任务:编译temp目录下的Hello.java文件
		JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();// 取得当前系统编译器
		int result = javaCompiler.run(null, null, null, "-d", "./temp", "./temp/Hello.java");
		// int result=javaCompiler.run(null, null, null, "Hello.java");
		System.out.println(result == 0 ? "恭喜编译成功!" : "对不起编译失败");

		// 在程序中来运行Hello.classs
		Runtime run = Runtime.getRuntime();
		Process process = run.exec("java -cp  ./temp my.xyz.hello.Hello");
		InputStream in = process.getInputStream();
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		String info = "";
		while ((info = reader.readLine()) != null) {
			System.out.println(info);
		}

	}

}

3 使用StandardJavaFileManager编译Java源程序

    在第一部分我们讨论调用java编译器的最容易的方法。这种方法能非常好地工作,但他确不能更有效地得到我们所需要的信息,如标准的输入、输出信息。而在Java SE6中最佳的方法是使用 StandardJavaFileManager类。这个类能非常好地控制输入、输出,并且 能通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener的实现。使用 StandardJavaFileManager需要两步。首先建立一个 DiagnosticCollector实例及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。最后通过CompilationTask中的call方法编译源程序。每个类的具体方法参数可以查看jase6 API文档。上面有很详细的解释。完整的演示代码如下

3.1  Eclipse工程截图

3.2   DynamicCompileTest.java

package my.xyz.test;
import java.io.File;
import java.io.FileWriter;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class DynamicCompileTest {
	public static void main(String[] args) throws Exception {
		//1.创建需要动态编译的代码字符串
		String nr="\r\n";//回车换行
		String source="package my.xyz.hello;"+nr+
		"    public class Hello{"+nr+

			"     public static void main(String[] args){"+nr+

			"     System.out.println(\"helloworld!\");"+nr+
		"}"+nr+
		"}"	;
		//2.将预动态编译的代码写入文件中1:创建临时目录 2:写入临时文件
		File dir=new File(System.getProperty("user.dir")+"/temp");//临时目录
		//如果/temp目录不存在创建temp目录
		if(!dir.exists()){
			dir.mkdir();
		}
		FileWriter writer=new FileWriter(new File(dir,"Hello.java"));
		writer.write(source);//将字符串写入文件中
		writer.flush();
		writer.close();
		//3:取得当前系统java编译器
		JavaCompiler javaCompiler=ToolProvider.getSystemJavaCompiler();
		//4:获取一个文件管理器StandardJavaFileManage
		StandardJavaFileManager javaFileManager=javaCompiler.getStandardFileManager(null, null, null);
		//5.文件管理器根与文件连接起来
		Iterable it=javaFileManager.getJavaFileObjects(new File(dir,"Hello.java"));
		//6.创建编译的任务
		//CompilationTask task=javaCompiler.getTask(null, javaFileManager, null,null, null, it);
		CompilationTask task=javaCompiler.getTask(null, javaFileManager, null,Arrays.asList("-d","./temp"), null, it);
		//执行编译
		task.call();
		javaFileManager.close();
	}
}

4  从内存中动态编译java程序并动态加载执行

    JavaCompiler不仅能编译硬盘上的 Java文件,而且还能编译内存中的 Java代码,然后使用reflection来运行他们。完整演示代码如下

4.1  Eclipse工程截图

4.2  JavaStringObject.java

package my.xyz;
import java.io.IOException;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
public class JavaStringObject extends SimpleJavaFileObject {
	private String code;
	public JavaStringObject(String name, String code) {
		//super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
		super(URI.create(name+".java"), Kind.SOURCE);
		this.code = code;
	}
	@Override
	public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
		return code;
	}
}

4.3 DynamicCompileTest.java

package my.xyz.test;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import my.xyz.JavaStringObject;
public class DynamicCompileTest {
	public static void main(String[] args) throws Exception {
		/*
		 * 编译内存中的java代码
		 */
		// 将代码写入内存中
		StringWriter writer = new StringWriter();// 内存字符串输出流
		PrintWriter out = new PrintWriter(writer);
		out.println("package my.xyz.hello;");
		out.println("public class Hello{");
		out.println("public static void main(String[] args){");
		out.println("System.out.println(\"helloworld!\");");
		out.println("}");
		out.println("}");
		out.flush();
		out.close();
		// 开始编译
		JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
		JavaFileObject fileObject = new JavaStringObject("Hello", writer.toString());
		CompilationTask  task=javaCompiler.getTask(null, null, null, Arrays.asList("-d","./bin"), null, Arrays.asList(fileObject));
		boolean success=task.call();
	     if(!success){
	    	 System.out.println("编译失败!");
	     }
	     else{
	    	 System.out.println("编译成功!");
	    	 //利用反射调用其中的main()方法
	    	  // Class class1=Class.forName("com.ynxinhua.hello.Hello");
	    	 //ClassLoader是自动去从当前工作目录下的classpath路径下去找 也就是bin目录下
	         //Class class1=ClassLoader.getSystemClassLoader().loadClass("my.xyz.hello.Hello");
	    	 //利用URLClassLoader去实例化一个Class类  类文件可以放在任意位置,这样就很方便了
	    	 URL[] urls=new URL[]{new URL("file:/"+"./bin/")};
	    	 URLClassLoader classLoader=new URLClassLoader(urls);
	          Class class1=classLoader.loadClass("my.xyz.hello.Hello");
	    	 Method method=class1.getDeclaredMethod("main",String[].class);
	    	 String[] args1={null};
	    	 method.invoke(class1.newInstance(),args1);
	     }
	}
}

java动态编译笔记的更多相关文章

  1. java动态编译 (java在线执行代码后端实现原理)(二)

    在上一篇java动态编译 (java在线执行代码后端实现原理(一))文章中实现了 字符串编译成字节码,然后通过反射来运行代码的demo.这一篇文章提供一个如何防止死循环的代码占用cpu的问题. 思路: ...

  2. java动态编译 (java在线执行代码后端实现原理)

    需求:要实现一个web网页中输入java代码,然后能知道编译结果以及执行结果 类似于菜鸟java在线工具的效果:https://c.runoob.com/compile/10 刚开始从什么概念都没有到 ...

  3. Java动态编译技术原理

    这里介绍Java动态编译技术原理! 编译,一般来说就是将源代码转换成机器码的过程,比如在C语言中中,将C语言源代码编译成a.out,,但是在Java中的理解可能有点不同,编译指的是将java 源代码转 ...

  4. Java 动态编译--DynamicCompiler

    java 动态编译自己写过程的机会比较少,记录一下: package com.xzlf.dynamicCompile; import java.io.IOException; import java. ...

  5. (转载)JAVA动态编译--字节代码的操纵

    在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发模式背后的过程是:开发人员编写的 ...

  6. Java动态编译

    程序产生过程 下图展示了从源代码到可运行程序的过程,正常情况下先编译(明文源码到字节码),后执行(JVM加载字节码,获得类模板,实例化,方法使用).本文来探索下当程序已经开始执行,但在.class甚至 ...

  7. java动态编译类文件并加载到内存中

    如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.cnblogs.com/anai/p/4270 ...

  8. Java 动态编译组件 & 类动态加载

    1.JDK6 动态编译组件 Java SE 6 之后自身集成了运行时编译的组件:javax.tools,存放在 tools.jar 包里,可以实现 Java 源代码编译,帮助扩展静态应用程序.该包中提 ...

  9. 动态代理 原理简析(java. 动态编译,动态代理)

    动态代理: 1.动态编译 JavaCompiler.CompilationTask 动态编译想理解自己查API文档 2.反射被代理类 主要使用Method.invoke(Object o,Object ...

随机推荐

  1. css实现文本溢出显示...

    在网页中显示文字内容时,经常会碰到文字内容特别长的情况,那么这个时候为了使网页看起来比较美观和简洁,会对内容进行处理.下面我们就来看一看,如何使用css来对文字溢出部分增加.... 首先来看第一种情况 ...

  2. 最好的5个Android ORM框架

    在开发Android应用时,保存数据有这么几个方式, 一个是本地保存,一个是放在后台(提供API接口),还有一个是放在开放云服务上(如 SyncAdapter 会是一个不错的选择). 对于第一种方式, ...

  3. Dreamweaver 扩展开发:文档路径等信息的处理

    //browseFile(fieldToStoreURL){ //getFullPath(filePathURL){ //getSimpleFileName() { //fixUpPath(docUR ...

  4. Oracle位图索引

    索引由KEY和Data组成 位图索引的KEY比普通非唯一性索引多包含一个组成部分,分区,分区是将数据按行由内部机制分段以达到比较好的检索效率 位图索引的Data中,该索引KEY中数据值在分区段中按行分 ...

  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(43)-工作流设计-字段分类设计

    系列目录 建立好42节的表之后,每个字段英文表示都是有意义的说明.先建立,就知道表的关系和用处了,当然,我的设计只是一个参考,你可能有很多改进的地方. 我们的工作流具体细节流程是这样的: 最终我们的模 ...

  6. 软件工程的引入:Scrum开发框架总结

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点如下: 软件工程概念 敏捷开发过程scrum 一.什么是软件工程?请用一句话描述. 软件工程是一门研究性的学科:它用工程化 ...

  7. 小萝贝控机大师工具推荐(一款在PC就能控制手机界面的工具)

    在一次写博客的过程中,要截取手机app上的几张图片,然后粘贴到博客里面去,不了解这个工具的时候,我就从手机上截图(使用其他的截图app或者使用手机自己的截图功能),然后再传送到电脑上,然后再放到博文中 ...

  8. php内核分析(四)-do_cli

    这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux # main 把剩下的代码增加了下注释全部贴出来了(这个是简化后的main函数,去掉了一些无关紧要的代码段): int m ...

  9. DataAccess通用数据库访问类,简单易用,功能强悍

    以下是我编写的DataAccess通用数据库访问类,简单易用,支持:内联式创建多个参数.支持多事务提交.支持参数复用.支持更换数据库类型,希望能帮到大家,若需支持查出来后转换成实体,可以自行扩展dat ...

  10. DataTable与DTO对象的简易转换类

    在web开发过程中,有时候为了数据传输的方便,比如:后台需要更新前端的ViewModel,此时我们定义一个与前端ViewModel结构一样的DTO对象,从数据层获取数据后,将数据封装成DTO然后序列化 ...