Java实现类似eval()函数或exec()函数的功能
在Python中有⼀个exec()函数,同样在JavaScript中有⼀个eval()函数,这两个函数有⼀个相似的特点,那就是可以在⾥⾯传⼊⼀段Python代码或者JavaScript代码,发现竟然可以运⾏该代码。
但是遗憾的是,Java中并不存在这样的函数,于是突发奇想,我们可不可以在Java中实现⼀个类似的函数,⽤来执⾏Java代码呢?
我们知道,Python和JavaScript属于脚本语⾔,也就是⾮编译型语⾔,它们并不存在先编译后执⾏的过程的。⽽Java、C++这种编译型的语⾔,⼀般都是先编译,后执⾏。对于Java来说,编译⽣成.class⽂件,然后JVM运⾏.class⽂件。⽽我们如果想要将Java代码传⼊⽅法中,然后运⾏,那么就不能采⽤传统的编译+运⾏了,采⽤Java提供的动态编译和动态加载的机制。
1、动态编译。
我们可以调⽤Process执⾏javac。但这种⽅式坦⽩来说不好。因为javac的命令参数写法和操作系统有关,也就是windows和linux的写法有少量不同。后来发现jdk提供⼀个动态编译的类。
JavaCompiler javac;
javac = SystemJavaCompiler();
int compilationResult = javac.run(null,null,null, "-g","-verbose",javaFile);
这样就可以动态进⾏编译。前两个参数是输⼊参数、输出参数,我觉得没有什么⽤,第三个参数是编译输出信息,默认输出到
⾥⾯。从第四个参数开始,就是javac的参数,可以⽤数组,也可以直接逗号分割。
2、动态加载。
动态加载实际就是调⽤ClassLoader。当然需要反射机制调⽤其中的⼀个内部⽅法,使之变成外部可调⽤的⽅法。
File file = new File("/Users/yangming/Work/DevWorkSpace/ssac/gx_hx/test/");
URLClassLoader classloader = (URLClassLoader) SystemClassLoader();
Method add = DeclaredMethod("addURL", new Class[]{URL.class});
add.setAccessible(true); //表⽰Method在使⽤时应该取消Java语⾔的访问权限检查
add.invoke(classloader, new Object[]{URI().toURL()});
Class c = classloader.loadClass("Test");
Object o = c.newInstance();
Method m = c.getDeclaredMethod("getString");
m.invoke(o, null);
这样就完成了类的动态加载。
下⾯我通过⼀个简单的例⼦加以说明,写⼀个eval()⽅法,可以执⾏System.out.println("Hello, " + str);这⾏代码:
Eval.java:
package com.darrenchan;
import flect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
ls.JavaCompiler;
ls.JavaFileObject;
ls.SimpleJavaFileObject;
ls.StandardJavaFileManager;
java python是什么意思ls.ToolProvider;
public class Eval {
/*
* 从java6版本开始,已经⽀持动态编译了,你可以在运⾏期直接编译.java⽂件,执⾏.class⽂件,并且能够获得相关的输⼊输出,
* 甚⾄还能监听相关的事件。
* java的动态编译提供了多种渠道,⽐如,可以动态编译⼀个字符串,也可以是⽂本⽂件,也可以是编译过的字节码⽂件(.class⽂件),
* 甚⾄可以是存放在数据库中的明⽂代码或字节码,只要是符合java规范的就都可以在运⾏期动态加载,其实现⽅式就是实现JavaFileObject
* 甚⾄可以是存放在数据库中的明⽂代码或字节码,只要是符合java规范的就都可以在运⾏期动态加载,其实现⽅式就是实现JavaFileObject * 接⼝,重写getCharContent、openInputStream、openOutputStream,或者实现JDK
* 已经提供的两个SimpleJavaFileObject、ForwardingJavaFileObject。下⾯我演⽰⼀下,如何动态编译⼀个字符串。
*/
/**
* Java动态编译演⽰
*/
public static void main(String[] args) throws Exception {
// Java源代码
String sourceStr = "public class Hello{public String sayHello(String name){return \"Hello, \"+name;}}";
// 类及⽂件名
String clsName = "Hello";
// ⽅法名
String methodName = "sayHello";
/**
* 当前编译器:注意,如果是⽤的jdk1.6的版本(建议使⽤jdk1.7,1.7是没有任何问题的),ToolProvider.
* getSystemJavaCompiler()拿到的对象将会为null,
* 原因是需要加载的Tools.jar不在jdk安装⽬录的jre⽬录下,需要⼿动将lib⽬录下的该jar包拷贝到jre下去,详情请参考:
* wwwblogs/fangwenyu/archive/2011/10/12/2209051.html
*/
JavaCompiler cmp = SystemJavaCompiler();
// Java标准⽂件管理器
StandardJavaFileManager fm = StandardFileManager(null, null,
null);
// Java⽂件对象
JavaFileObject jfo = new StringJavaObject(clsName, sourceStr);
// 编译参数,类似于javac <options> 中的options
List<String> optionsList = new ArrayList<String>();
// 编译⽂件的存放地⽅,注意:此处是为Eclipse⼯具特设的
optionsList.addAll(Arrays.asList(new String[] { "-d", "./bin" }));
// 要编译的单元
List<JavaFileObject> jfos = Arrays.asList(new JavaFileObject[] { jfo });
// 设置编译环境
JavaCompiler.CompilationTask task = Task(null, fm, null,
optionsList, null, jfos);
// 编译成功
if (task.call()) {
// ⽣成对象
Object obj = Class.forName(clsName).newInstance();
Class<? extends Object> cls = Class();
// 调⽤sayHello⽅法
Method m = Method(methodName, String.class);
// 第⼀个参数是执⾏该⽅法的主调,后⾯若⼲个参数是执⾏该⽅法时传⼊该⽅法的实参
String str = (String) m.invoke(obj, "陈驰");
System.out.println(str);
}
}
}
StringJavaObject.java:
package com.darrenchan;
import java.io.IOException;
import java.URI;
ls.SimpleJavaFileObject;
public class StringJavaObject extends SimpleJavaFileObject {
/**
* 源代码
*/
private String content = "";
/**
* 遵循Java规范的类名及⽂件
*/
public StringJavaObject(String javaFileName, String content){
super(_createStringJavaObjectUri(javaFileName), Kind.SOURCE);
}
/**
* 产⽣⼀个URL资源路径
*/
private static URI _createStringJavaObjectUri(String javaFileName) {
//注意此处未设置包名
ate("String:///" + javaFileName + sion); }
/**
* ⽂本⽂件代码
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
}
经过测试,最终的运⾏结果符合预期,如下所⽰:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论