专栏首页数据之美java 利用反射模拟动态语言的 eval 函数

java 利用反射模拟动态语言的 eval 函数

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class Eval {
	public static Object eval(String str) throws Exception {
		StringBuffer sb = new StringBuffer();
		sb.append("public class Temp");
		sb.append("{");
		sb.append("    public Object getObject()");
		sb.append("    {");
		sb.append("        " + str + "return new Object();");
		sb.append("    }");
		sb.append("}");
		// 调用自定义类加载器加载编译在内存中class文件
		// 说明:这种方式也需要些数据落地写磁盘的
		// 为毛一定要落地呢,直接内存里加载不就完了嘛
		// 应该也是可以的,它从磁盘读了也是进内存
		// 只不过java不允许直接操作内存
		// 写jni估计是可以
		Class clazz = new MyClassLoader().findClass(sb.toString());
		Method method = clazz.getMethod("getObject");
		// 通过反射调用方法
		return method.invoke(clazz.newInstance());
	}

	public static void main(String[] args) throws Exception {
		Object rval = eval("System.out.println(\"Hello World\");");
		System.out.println(rval);
	}
}


/*http://hi.baidu.com/rqzmvfodkxadine/item/a789f2117af5474ee65e0657
http://blog.csdn.net/leeyohn/article/details/5179422
http://www.99inf.net/SoftwareDev/Java/33737.htm
*/

/*public class Eval
{
    public static void main(String[] args)throws Exception
    {
         Object rval = eval("System.out.println(\"Hello World\");return 5;");
         System.out.println(rval);
    }
    public static Object eval(String str)throws Exception
    { 
         //生成Java源文件 
         StringBuilder s = new StringBuilder("public class Temp{"); 
         s.append("        public Object rt(){"); 
         s.append("                " + str); 
         s.append("        }"); 
         s.append("}"); 
         //在当前目录生成Java源文件
         File f = new File("Temp.java"); 
         PrintWriter pw = new PrintWriter(new FileWriter(f)); 
         pw.println(s.toString()); 
         pw.close(); 
         //动态编译(此处可直接编译内存中的Java源码,二进制码也放在内存中)
         //使用这些动态编译的方式的时候,需要确保JDK中的tools.jar在应用的 CLASSPATH中。
         com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main(); 
         // 这里eclipse寻找class的路径就是在bin下面找的, 需要把class编译到项目的 bin目录下
         String[] cpargs = new String[] {"-d", "./bin" ,"Temp.java"}; 
         //动态编译
         int status = javac.compile(cpargs); 
         if(status != 0 )
         { 
                 System.out.println("您给的Java代码有错!"); 
                 return null; 
         } 
         //创建一个URL数组
         URL[] urls = {new URL("file:Temp.class")};
         //以默认的ClassLoader作为父ClassLoader,创建URLClassLoader
         URLClassLoader myClassLoader = new URLClassLoader(urls);
         //加载Temp类(如果要加载内存中的class文件-二进制码,需要自己写类加载器)
         Class clazz = myClassLoader.loadClass("Temp"); 
         //获取rt方法
         Method rt = clazz.getMethod("rt"); 
         //动态调用rt方法
         return rt.invoke(clazz.newInstance()); 
    } 
}*/
import java.util.Arrays;

import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.SimpleJavaFileObject;
import javax.tools.JavaFileObject;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.DiagnosticCollector;
import java.net.URI;
import java.net.URISyntaxException;

public class MyClassLoader extends ClassLoader {
	@Override
	public Class<?> findClass(String str) throws ClassNotFoundException {
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		// 用于诊断源代码编译错误的对象
		DiagnosticCollector diagnostics = new DiagnosticCollector();
		// 内存中的源代码保存在一个从JavaFileObject继承的类中
		JavaFileObject file = new JavaSourceFromString("Temp", str.toString());
		// System.out.println(file);
		Iterable compilationUnits = Arrays.asList(file);
		// 关于报:Exception in thread "main" java.lang.ClassNotFoundException: Temp
		// 的解决方法:http://willam2004.iteye.com/blog/1026454
		// 需要为compiler.getTask方法指定编译路径:
		// 执行过程如下:
		// 1、定义类的字符串表示。
		// 2、编译类
		// 3、加载编译后的类
		// 4、实例化并进行调用。
		// 在eclipse下如果按照上述的方式进行调用,会在第三步中加载编译的类过程抛出“ClassNotFoundException”。
		// 因为默认的Eclipse的java工程编译后的文件是放在当前工程下的bin目录下。而第二步编译输出的路径是工程目录下,
		// 所以加载时会抛出类找不到的错误。
		String flag = "-d";
		String outDir = System.getProperty("user.dir") + "/" + "bin";
		Iterable<String> stringdir = Arrays.asList(flag, outDir); // 指定-d dir 参数
		// 建立一个编译任务
		JavaCompiler.CompilationTask task = compiler.getTask(null, null, null,
				stringdir, null, compilationUnits);
		// 编译源程序
		boolean result = task.call();
		if (result) {
			return Class.forName("Temp");
		}
		return null;
	}

}

class JavaSourceFromString extends SimpleJavaFileObject {
	private String name;
	private String code;

	public JavaSourceFromString(String name, String code) {
		super(URI.create("string:///" + name.replace('.', '/')
				+ Kind.SOURCE.extension), Kind.SOURCE);
		this.code = code;
	}

	public CharSequence getCharContent(boolean ignoreEncodingErrors) {
		return code;
	}
}

Refer:

[1] Java 类的热替换 —— 概念、设计与实现

https://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/index.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 制作Aspose CHM文档的过程记录

    最近公司需要使用Aspose组件开发相关内容,但是网上找不到理想的参考文档,官网访问速度也慢的可以。所以打算自己做份CHM文档,做的过程中遇到很多困难,这里记录...

    用户1221057
  • Java虚拟机

    1、Java虚拟机是什么 “Java虚拟机“可以指三种不同的东西 抽象规范 一个具体的实现 一个运行中的虚拟机实例 当运行一个Java程序的同时,也就是在运行一...

    java达人
  • (67) 线程的基本协作机制 (上) / 计算机程序的思维逻辑

    上节介绍了多线程之间竞争访问同一个资源的问题及解决方案synchronized,我们提到,多线程之间除了竞争,还经常需要相互协作,本节就来介绍Java中多线程协...

    swiftma
  • (66) 理解synchronized / 计算机程序的思维逻辑

    上节我们提到了多线程共享内存的两个问题,一个是竞态条件,另一个是内存可见性,我们提到,解决这两个问题的一个方案是使用synchronized关键字,本节就来讨论...

    swiftma
  • Java开发者编写SQL语句时常见的10种错误

    Java开发者对于面向对象编程思维与命令行编程思维的协调程度,取决于他们如下几种能力的水平: 1. 技巧(任何人都可以编写命令行形式的代码) 2. 教条(有的...

    java达人
  • HashMap的工作原理

    HashMap的工作原理是近年来常见的Java面试题。几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和Hash...

    java达人
  • Calendar类中add/set/roll方法的区别

    Calendar类中有三个方法更改日期的某个字段:set()、add() 和 roll()。 set(f, value) 将日历字段 f 更改为 value。此...

    用户1221057
  • Java 枚举7常见种用法

    用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl…. 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,...

    java达人
  • (60) 随机读写文件及其应用 - 实现一个简单的KV数据库 / 计算机程序的思维逻辑

    查看历史文章,请点击上方链接关注公众号。 57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读...

    swiftma
  • java反射机制

    基本概念   在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?   答案是肯定的。   这种...

    java达人

扫码关注云+社区

领取腾讯云代金券