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;
}
}
[1] Java 类的热替换 —— 概念、设计与实现
https://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/index.html