JAVA反射机制+动态运行编译期不存在的JAVA程序

一、有关JAVA反射

在运行期间,在不知道某个类A的内部方法和属性时,能够动态的获取信息、获取类或对象的方法、属性的功能,称之为反射。

1.相关类:

(1)Class

(2)Method

(3)Field

2.相关方法:

(1)Class.forName("<完整包名+类名>");  返回对应的类(同一JVM不管创建该类的多少对象,类只有一个)。

(2)class.getMethod(要获取的方法名,参数类型,参数类型.....);  返回该方法。

(3)执行方法method.invoke(实例,参数,参数.....)  返回该方法的返回值(Object类型,可强转为指定类型)。

(4)class.newInstance()  返回一个实例对象,通常与(3)一起使用。

(5)class.getFields()  返回该对象的所有共有属性。

(6)class.getDeclaredFields  获取对象的共有+私有属性。

二、应用

1.简介:

公司有个项目,需要将提交上来的Java源码进行编译与运行,并返回结果。一般的思想是JVM-命令行-JVM的模式。但是这样不但执行速度慢,且获取编译异常、运行异常、运行结果都是非常困难的。所以通过JAVA反射机制,以及自带的编译工具完成功能。

2.代码:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; public class RunStudentPro {
private static String CLASSPATH;
/**
* 程序运行成功
*/
private static final boolean SUCCESS = true;
/**
* 程序运行失败
*/
private static final boolean DEFAULT = false; /**
* 编译期的错误列表
*/
private List<String> bianYiError; /**
* 学生运行的结果
*/
private Object studenResult; /**
* 运行时异常
*/
private List<String> runTimeErrors; /**
* 运行学生程序的主方法
*
* @param pro
* 学生代码
* @param javaName
* 学生类名
* @param methodName
* 学生方法
* @return
*/
public boolean runStudentProgram(String pro, String javaName, String methodName, String studentID) {
// 获取类所在的路径
CLASSPATH = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); if (System.getProperty("os.name").contains("dows")) {
CLASSPATH = CLASSPATH.substring(1, CLASSPATH.length());
} if (CLASSPATH.endsWith(".jar")) {
CLASSPATH = CLASSPATH.substring(0, CLASSPATH.lastIndexOf("/") + 1);
} CLASSPATH = CLASSPATH + studentID + "/"; // 在本地写入java文件
writejavaFile(CLASSPATH, javaName, pro);
// 编译java文件
boolean javaCompilerFile = JavaCompilerFile(javaName); if (!javaCompilerFile) {
// 编译期失败
return DEFAULT;
} boolean runStudentProgram = runStudentProgram(javaName, methodName); if (!runStudentProgram) {
// 运行失败
return DEFAULT;
} return SUCCESS;
} /**
* 编译学生的java文件
*
* @param javaName
* @return
*/
private boolean JavaCompilerFile(String javaName) {
StandardJavaFileManager javafile = null;
boolean result = SUCCESS; try {
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
javafile = javac.getStandardFileManager(null, null, null);
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
String filename = CLASSPATH + javaName + ".java";
Iterable<? extends JavaFileObject> units = javafile.getJavaFileObjects(filename);
CompilationTask t = javac.getTask(null, javafile, collector, null, null, units); if (!t.call()) {
List<Diagnostic<? extends JavaFileObject>> diagnostics = collector.getDiagnostics();
bianYiError = new ArrayList<String>();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
bianYiError.add(diagnostic.toString());
}
result = DEFAULT;
}
} finally {
try {
if (javafile != null) {
javafile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} return result;
} /**
* 运行学生的程序
*
* @param javaName
* 主类名
* @param methodName
* 方法名
* @return
*/
private boolean runStudentProgram(String javaName, String methodName) {
URLClassLoader classload = null;
boolean result = SUCCESS; try {
URL url = new URL("file:/" + CLASSPATH);
URL[] urls = new URL[] { url };
classload = new URLClassLoader(urls);
Class<?> clazz = classload.loadClass(javaName);
Method method = clazz.getMethod(methodName);
studenResult = method.invoke(clazz.newInstance());
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | InstantiationException
| MalformedURLException e) {
runTimeErrors = new ArrayList<String>();
runTimeErrors.add(getExceptionAllinformation(e));
result = DEFAULT;
} finally {
if (classload != null) {
try {
classload.close();
} catch (IOException e) {
e.printStackTrace();
result = DEFAULT;
}
}
} return result;
} /**
* 获取异常的所有信息并转换为字符串
*
* @param ex
* @return
*/
private static String getExceptionAllinformation(Exception ex) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream pout = new PrintStream(out);
ex.printStackTrace(pout);
String ret = new String(out.toByteArray());
pout.close();
try {
out.close();
} catch (Exception e) {
}
return ret;
} /**
* 创建java文件
*
* @param path
* 路径
* @param javaName
* 文件名
* @param pro
* 程序字符串
*/
private static void writejavaFile(String path, String javaName, String pro) {
File dir = new File(path); if (!dir.exists()) {
dir.mkdir();
} File file = new File(path + javaName + ".java"); if (file.exists()) {
file.delete();
}
FileWriter fw = null;
try {
file.createNewFile();
fw = new FileWriter(file);
fw.write(pro);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 获取编译时的错误信息
*
* @return
*/
public List<String> getBianYiError() {
return bianYiError;
} /**
* 获取学生运行后的结果
*
* @return
*/
public Object getStudenResult() {
return studenResult;
} /**
* 获取学生运行时的异常
*
* @return
*/
public List<String> getRunTimeErrors() {
return runTimeErrors;
}
}

3.详细讲解:

(1)程序入口为public boolean runStudentProgram(String pro, String javaName, String methodName, String studentID)

通过传入代码、主类名称、要执行的方法名、学生ID,进行保存代码,编译代码、运行代码。

其中,this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()获取当前类所在的目录(主要是方便移植,此路径可写死或修改为指定目录),然后将.jar及开头的'/'符号去除。

(2)编译学生代码:

通过JavaCompiler类进行编译(JDK1.6提供),使用方式为JavaCompiler javac = ToolProvider.getSystemJavaCompiler();

注意的是,编译器接口是在javax.tools包里,所以可能IDE所配置的JRE不存在此类,所以报错的话直接更改JRE。

可以通过CompilationTask t = javac.getTask(null, javafile, collector, null, null, units);中设置collector获取编译的错误信息。

其中编译方法的运行为call()方法,也可以javac.run();

(3)运行学生代码:

method.invoke(clazz.newInstance());因为我们规定了不允许传入参数,所以此处只是传入了实例,并没有传入参数。

运行时的错误信息,可以通过异常Exception指定输出流进行保存。本程序的getExceptionAllinformation方法即为实现方法。

三、运行效果截图

1.正常运行,传入的代码如下

运行结果如下:

2.模拟编译时异常,传入代码如下:

运行结果如下:

3.模拟运行时异常,传入代码如下:

运行结果如下:

最新文章

  1. Android数据存储-文件操作
  2. 安装第三方库出现 Python version 2.7 required, which was not found in the registry
  3. Openstack安全规则说明
  4. nginx 版本介绍
  5. EMVTag系列15《选择应用响应数据》
  6. hdu 4859 海岸线 最小割
  7. MySQL数据库下用户及用户权限配置
  8. tty、pty、pts等(小记)
  9. A题笔记(1)
  10. 雅虎UED--无障碍网页设计
  11. .NET Core 1.0、ASP.NET Core 1.0和EF Core 1.0简介
  12. SQL2005性能分析一些细节功能你是否有用到?(二)
  13. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
  14. 腾讯云中ssL证书的配置安装
  15. Docker中mysql大小写敏感配置不起作用的问题排查
  16. django默认模板引擎和jinja2模板引擎
  17. mysql慢日志, 锁表情况查询
  18. Spring配置从配置文件读取属性值
  19. MySQL Event--Event and EventScheduler
  20. springmvc转页面

热门文章

  1. Maven的pom.xml文件结构之基本配置parent和继承结构
  2. Source object main.o has EABI version 0, but target ../../../bin/ad has EABI version 5
  3. vue-cli3使用cdn引入
  4. C#5.0 异步编程 Async和Await--介绍
  5. 记录一下取ul中li的值
  6. [转载]Ubuntu Server下配置UTF-8中文环境
  7. 记录常用的linux命令
  8. Vue学习笔记【18】——Vue中的动画(使用过渡类名)
  9. PHP FILTER_SANITIZE_URL 过滤器
  10. C++11下的关键字