转自 http://blog.youkuaiyun.com/shan9liang/article/details/40451235
一般的java的动态编译是需要先生成java文件,然后编译成class,最后用classloader加载进来,生成最终的实例。
我在这里介绍一种方法,不需要任何java类文件,将字符串输入到内存,然后编译,加载,执行,整个过程全部在内存中实现,
不会产生.java和.class文件,做到了洁净无污染。
环境:
jdk 1.6
为什么是1.6?
早期的版本中(Java 5及以前版本)中只能通过tools.jar中的com.sun.tools.javac包来调用Java编译器,但由于tools.jar不是标准的Java库,在使用时必须要设置这个jar的路径。而在Java 6中为我们提供了标准的包来操作Java编译器,这就是javax.tools包。使用这个包,我们可以不用将jar文件路径添加到classpath中了。
思路:
1、常规做法,也是
调用java编译器最简单的方法:生成 .java文件,编译生成 .class文件。
(1)通过ToolProvider类的静态方法getSystemJavaCompiler来得到一个JavaCompiler接口的实例。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
(2)JavaCompiler中最核心的方法是run。通过这个方法可以编译java源程序。这个方法有3个固定参数和1个可变参数(可变参数是从Jave SE5开始提供的一个新的参数类型,用type… argu表示)。前3个参数分别用来为java编译器提供参数、得到Java编译器的输出信息以及接收编译器的错误信息,后面的可变参数可以传入一个或多个Java源程序文件。如果run编译成功,返回0。
int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和System.err。如果我们要编译一个test.java文件,并将使用标准输入输出,run的使用方法如下:
int results = tool.run(null, null, null, "test.java");
很简单,但在Java 6中最好的方法是使用StandardJavaFileManager类。这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener的实现。
使用StandardJavaFileManager需要两步。首先建立一个DiagnosticCollector实例以及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。最后通过CompilationTask中的call方法编译源程序。
2、我们要做的,就是不生成 .java文件,不生成 .class文件,一切都在内存中运行。
其实,很简单,只要在上述思路的基础上再加上一步,就一小步。
(1)
我们可以编写一个 CharSequenceJavaFileObject extends SimpleJavaFileObject
,通过这个类可以输入Java源代码字符串哦。
核心代码:
-
-
-
-
-
-
-
-
-
- public Object javaCodeToObject(String fullClassName, String javaCode) throws IllegalAccessException, InstantiationException {
- long start = System.currentTimeMillis();
- Object instance = null;
-
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-
- DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
-
-
-
- ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));
-
- List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
- jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode));
-
-
- List<String> options = new ArrayList<String>();
- options.add("-encoding");
- options.add("UTF-8");
- options.add("-classpath");
- options.add(this.classpath);
-
- JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
-
- boolean success = task.call();
-
- if (success) {
-
- JavaClassObject jco = fileManager.getJavaClassObject();
- DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
- Class clazz = dynamicClassLoader.loadClass(fullClassName,jco);
- instance = clazz.newInstance();
- } else {
-
- String error = "";
- for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
- error = error + compilePrint(diagnostic);
- }
- }
- long end = System.currentTimeMillis();
- System.out.println("javaCodeToObject use:"+(end-start)+"ms");
- return instance;
- }
什么动态生成.java文件,生成.class文件,都是浮云。一切都在内存中搞定,不用生成任何文件,做到零污染。