`

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

阅读更多

实现步骤:

1.自定义一个Java类,该Java类中定义一个方法来包含需要被运行的代码。
2.动态编译刚刚生成的Java源码,不在磁盘上生成源码,而是直接编译内存中的Java源码。
3.动态加载刚刚创建编译的Java二进制码,编译好的Java二进制码不是在磁盘上,而是放在内存中,并定义自己的类加载器,负责加载内存中的class文件。
4.通过反射运行前一步加载的类。

package test.dynamic;

import java.lang.reflect.Method;

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);
	}

}

 

 

package test.dynamic;

import java.net.URI;
import java.util.Arrays;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

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;
	}

}

 

 

 

下面是把源代码生成在磁盘上再加载到内存中的实现

 

package test.dynamic;

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 Eval2 {

	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());

	}

}

 

分享到:
评论

相关推荐

    java实现eval函数

    在Java编程语言中,`eval`函数通常与JavaScript关联,因为JavaScript有一个内置的`eval`函数,它能够解析并执行一个字符串作为JavaScript代码。然而,Java本身并不直接提供类似的功能。`eval`函数在Java中并不是标准...

    在java中利用动态编译实现eval

    在Java中,实现类似`eval`功能的一种方法是利用Java的动态编译API,例如`javax.tools.JavaCompiler`接口和相关的工具类。以下是一个简单的示例,演示如何将字符串转换为Java方法并执行: ```java import javax....

    java动态特性eval

    Java动态特性eval的相关实现主要涉及Java的反射机制、表达式求值以及编译器接口。在JavaScript中,`eval()`函数能够将一个字符串作为代码执行,从而实现动态编程。Java中虽然没有直接对应的内置方法,但通过一些技术...

    Javascript中eval函数的用法

    在上面的例子中,我们看到了一种使用 eval 函数来动态生成 JavaScript 代码的方法。我们使用 prompt 函数来获取用户的输入,然后使用 eval 函数来将用户的输入作为 JavaScript 代码来执行。这样,我们可以根据用户的...

    eval函数的一些用法

    在IT行业中,`eval()`函数是一个非常特殊且强大的工具,主要在编程语言中使用,例如JavaScript、PHP等。它的功能是将字符串作为代码执行,这在处理动态生成的代码或者解析JSON数据时尤为有用。本篇文章将深入探讨`...

    java实现js中eval功能

    在Java中,没有直接对应的内置函数可以实现这样的功能,但我们可以构建一个类似的机制来模拟`eval()`的行为。 首先,我们需要理解`eval()`的核心功能:将字符串转换为可执行的代码。在Java中,这可以通过编译和执行...

    javascript 自定义eval函数实现

    最后,压缩包中的`FreeMarker设计指南.chm`文件名暗示了一个与JavaScript无关的话题,FreeMarker是一个用Java编写的模板语言,常用于生成HTML、XML等静态或动态内容。虽然这里没有直接关联,但在某些场景下,如...

    利用eval()函数给树节点统一添加单击事件实现新建选项卡功能

    这篇博客“利用eval()函数给树节点统一添加单击事件实现新建选项卡功能”探讨了一个具体的JavaScript编程技巧,如何通过eval()函数来处理用户交互,特别是针对树形结构数据的点击事件,进而实现新的选项卡功能。...

    用C语言实现eval函数.zip

    `eval`函数通常用于执行字符串形式的代码,这在C语言中并不直接支持,因为C是一种静态类型的语言,它的特性不包括在运行时解析和执行动态代码。 在C语言中,要实现类似的功能,我们需要理解编译原理、词法分析、...

    深入认识javascript中得eval函数

    ### 深入认识JavaScript中的eval函数 #### 一、eval函数概述 在JavaScript中,`eval`函数是一个内置函数,它可以将字符串形式的代码解析并执行。这为开发者提供了一种灵活的方式来动态生成和执行代码。然而,由于...

    Python的eval函数写的eval-calc(计算器)

    Python的eval函数写的eval_calc(计算器) 进行了多重符号判断,可以在很大程度上帮助孩子们学习。

    java解析公式 eval.jar

    Java解析公式库`eval.jar`是一个专门为Java开发者设计的开源工具,它允许程序在运行时动态解析和执行数学表达式。这个库的核心功能是提供一个简单、高效的接口,用于处理包含加法(+)、减法(-)、乘法(*)、除法...

    javascript执行eval函数时利用正则表达式去掉回车符换行符和注释

    最后,关于文档`javascript执行eval函数时利用正则表达式去掉回车符换行符和注释.doc`,这可能是详细阐述这一过程的文档,包含了具体实现和可能遇到的问题的解决方案。阅读此文档将有助于深入理解如何实际应用这些...

    javascript中eval函数用法分析.docx

    虽然`eval()`函数提供了一种强大的动态执行代码的能力,但它也带来了安全隐患和性能问题。在现代JavaScript开发中,建议仅在必要时使用`eval()`函数,并优先考虑其他更安全和高效的替代方案。理解`eval()`函数的作用...

    Python实现四则运算模仿eval函数计算处理

    使用Python实现的四则运算,注释清晰,功能备注完整,主要练习函数递归和正则表达式,目前只能计算整数的四则运算,如需完善,可把...供初学Python的同学参考(实现过程没有使用eval函数,否则失去练习效果,无意义)。

    go-eval:Go 的 eval 函数,支持布尔和算术表达式

    它类似于动态语言中常见的 eval 函数的基本形式。 例子: res , err := EvalBool ( "1 &gt; 2" )log . Print ( res )&gt; false res , err := EvalBool ( `(1 + 3) &gt;= 4 && ("FOO" == "BAR" || "FOO" == "FOO")` )log . ...

    在Javascript中Eval函数的使用.doc

    `eval()`的一个实际应用是在动态创建和执行函数。比如,我们可以根据用户输入创建不同的行为。假设用户选择了一项操作,我们可以将选择转化为字符串,然后用`eval()`来执行相应的函数: ```javascript var ...

    在Javascript中Eval函数的使用?

    如果需要动态执行代码,考虑使用`new Function()`或`Function`构造函数,或者利用`setTimeout`和`setInterval`的回调函数,这些方法相对更安全,性能也更好。 在实际开发中,`eval()`的一个常见用途是在JSON解析中...

    Perl die、warn、eval函数使用总汇

    ### Perl中的Error Handling:Die、Warn、Eval 函数详解 #### 一、引言 在Perl编程语言中,错误处理是非常重要的一部分。错误处理能够帮助开发者有效地管理程序运行过程中可能出现的各种问题,比如文件操作失败、...

Global site tag (gtag.js) - Google Analytics