我是靠谱客的博主 痴情羊,最近开发中收集的这篇文章主要介绍自定义 编译 java_Java--自定义Class并且在内存中编译,加载,实例化,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

本文的目的:

使用者在程序运行期间,可以动态的写Java Class,不需要生成任何.Class文件就可以完全在内存中编译,加载,实例化。

1、需要用到的组件介绍

1)JavaCompiler:用于编译Java Code。

2)CharSequenceJavaFileObject:用于保存Java Code,提供方法给JavaCompiler获取String形式的Java Code。

3)ClassFileManager:用于JavaCompiler将编译好后的Class文件保存在指定对象中。

4)JavaClassObject:ClassFileManager告诉JavaCompiler需要将Class文件保存在JavaClassObject中,但是由JavaClassObject来决定最终以byte流来保存数据。

5)DynamicClassLoader:自定义类加载器,用于加载最后的二进制Class

2、源码展现:

CharSequenceJavaFileObject.java

packagecom.ths.platform.framework.dynamic;importjavax.tools.SimpleJavaFileObject;importjava.net.URI;/*** 用于将java源码保存在content属性中*/

public class CharSequenceJavaFileObject extendsSimpleJavaFileObject {/*** 保存java code*/

privateString content;/*** 调用父类构造器,并设置content

*@paramclassName

*@paramcontent*/

publicCharSequenceJavaFileObject(String className, String content){super(URI.create("string:///" + className.replace('.', '/')+Kind.SOURCE.extension), Kind.SOURCE);this.content =content;

}/*** 实现getCharContent,使得JavaCompiler可以从content获取java源码

*@paramignoreEncodingErrors

*@return

*/@Overridepublic String getCharContent(booleanignoreEncodingErrors) {returncontent;

}

}

ClassFileManager.java

packagecom.ths.platform.framework.dynamic;importjava.io.IOException;import javax.tools.*;/*** 类文件管理器

* 用于JavaCompiler将编译好后的class,保存到jclassObject中*/

public class ClassFileManager extendsForwardingJavaFileManager {/*** 保存编译后Class文件的对象*/

privateJavaClassObject jclassObject;/*** 调用父类构造器

*@paramstandardManager*/

publicClassFileManager(StandardJavaFileManager standardManager) {super(standardManager);

}/*** 将JavaFileObject对象的引用交给JavaCompiler,让它将编译好后的Class文件装载进来

*@paramlocation

*@paramclassName

*@paramkind

*@paramsibling

*@return*@throwsIOException*/@OverridepublicJavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)throwsIOException {if (jclassObject == null)

jclassObject= newJavaClassObject(className, kind);returnjclassObject;

}publicJavaClassObject getJavaClassObject() {returnjclassObject;

}

}

JavaClassObject.java

packagecom.ths.platform.framework.dynamic;importjavax.tools.SimpleJavaFileObject;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.net.URI;/*** 将输出流交给JavaCompiler,最后JavaCompiler将编译后的class文件写入输出流中*/

public class JavaClassObject extendsSimpleJavaFileObject {/*** 定义一个输出流,用于装载JavaCompiler编译后的Class文件*/

protected final ByteArrayOutputStream bos = newByteArrayOutputStream();/*** 调用父类构造器

*@paramname

*@paramkind*/

publicJavaClassObject(String name, Kind kind) {super(URI.create("string:///" + name.replace('.', '/') +kind.extension), kind);

}/*** 获取输出流为byte[]数组

*@return

*/

public byte[] getBytes() {returnbos.toByteArray();

}/*** 重写openOutputStream,将我们的输出流交给JavaCompiler,让它将编译好的Class装载进来

*@return*@throwsIOException*/@Overridepublic OutputStream openOutputStream() throwsIOException {returnbos;

}/*** 重写finalize方法,在对象被回收时关闭输出流

*@throwsThrowable*/@Overrideprotected void finalize() throwsThrowable {super.finalize();

bos.close();

}

}

DynamicEngine.java(职责:使用JavaCompiler编译Class,并且使用DynamicClassLoader加载Class)

packagecom.ths.platform.framework.dynamic;importjava.io.File;importjava.net.URL;importjava.net.URLClassLoader;importjava.util.ArrayList;importjava.util.List;importjavax.tools.Diagnostic;importjavax.tools.DiagnosticCollector;importjavax.tools.JavaCompiler;importjavax.tools.JavaFileObject;importjavax.tools.ToolProvider;/*** 在Java SE6中最好的方法是使用StandardJavaFileManager类。

* 这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,

* 而DiagnosticCollector类就是listener的实现。

* 使用StandardJavaFileManager需要两步。

* 首先建立一个DiagnosticCollector实例以及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。

* 最后通过CompilationTask中的call方法编译源程序。*/

public classDynamicEngine {//单例

private static DynamicEngine ourInstance = newDynamicEngine();public staticDynamicEngine getInstance() {returnourInstance;

}privateURLClassLoader parentClassLoader;privateString classpath;privateDynamicEngine() {//获取类加载器

this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();//创建classpath

this.buildClassPath();

}/*** @MethodName : 创建classpath*/

private voidbuildClassPath() {this.classpath = null;

StringBuilder sb= newStringBuilder();for (URL url : this.parentClassLoader.getURLs()) {

String p=url.getFile();

sb.append(p).append(File.pathSeparator);

}this.classpath =sb.toString();

}/*** @MethodName : 编译java代码到Object

* @Description : TODO

*@paramfullClassName 类名

*@paramjavaCode 类代码

*@returnObject

*@throwsException*/

public Object javaCodeToObject(String fullClassName, String javaCode) throwsException {long start = System.currentTimeMillis(); //记录开始编译时间

Object instance = null;//获取系统编译器

JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();//建立DiagnosticCollector对象

DiagnosticCollector diagnostics = new DiagnosticCollector();//建立用于保存被编译文件名的对象//每个文件被保存在一个从JavaFileObject继承的类中

ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));

List jfiles = new ArrayList();

jfiles.add(newCharSequenceJavaFileObject(fullClassName, javaCode));//使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合

List options = new ArrayList();

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{//如果想得到具体的编译错误,可以对Diagnostics进行扫描

String error = "";for(Diagnostic diagnostic : diagnostics.getDiagnostics()) {

error+=compilePrint(diagnostic);

}

}long end =System.currentTimeMillis();

System.out.println("javaCodeToObject use:"+(end-start)+"ms");returninstance;

}/*** @MethodName : compilePrint

* @Description : 输出编译错误信息

*@paramdiagnostic

*@return

*/

privateString compilePrint(Diagnostic diagnostic) {

System.out.println("Code:" +diagnostic.getCode());

System.out.println("Kind:" +diagnostic.getKind());

System.out.println("Position:" +diagnostic.getPosition());

System.out.println("Start Position:" +diagnostic.getStartPosition());

System.out.println("End Position:" +diagnostic.getEndPosition());

System.out.println("Source:" +diagnostic.getSource());

System.out.println("Message:" + diagnostic.getMessage(null));

System.out.println("LineNumber:" +diagnostic.getLineNumber());

System.out.println("ColumnNumber:" +diagnostic.getColumnNumber());

StringBuffer res= newStringBuffer();

res.append("Code:[" + diagnostic.getCode() + "]n");

res.append("Kind:[" + diagnostic.getKind() + "]n");

res.append("Position:[" + diagnostic.getPosition() + "]n");

res.append("Start Position:[" + diagnostic.getStartPosition() + "]n");

res.append("End Position:[" + diagnostic.getEndPosition() + "]n");

res.append("Source:[" + diagnostic.getSource() + "]n");

res.append("Message:[" + diagnostic.getMessage(null) + "]n");

res.append("LineNumber:[" + diagnostic.getLineNumber() + "]n");

res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]n");returnres.toString();

}

}

DynamicClassLoader.java

packagecom.ths.platform.framework.dynamic;importjava.net.URLClassLoader;importjava.net.URL;/*** 自定义类加载器*/

public class DynamicClassLoader extendsURLClassLoader {publicDynamicClassLoader(ClassLoader parent) {super(new URL[0], parent);

}public Class findClassByClassName(String className) throwsClassNotFoundException {return this.findClass(className);

}publicClass loadClass(String fullName, JavaClassObject jco) {byte[] classData =jco.getBytes();return this.defineClass(fullName, classData, 0, classData.length);

}

}

DynaCompTest.java(测试类,从myclass文件中读出源码并在内存中编译)

packagecom.ths.platform.framework.dynamic;importsun.misc.IOUtils;importjava.io.File;importjava.io.FileInputStream;importjava.io.InputStream;public classDynaCompTest

{public static void main(String[] args) throwsException {

String fullName= "com.seeyon.proxy.MyClass";

File file= new File("/Users/yangyu/Downloads/myclass");

InputStream in= newFileInputStream(file);byte[] bytes = IOUtils.readFully(in, -1, false);

String src= newString(bytes);

in.close();

System.out.println(src);

DynamicEngine de=DynamicEngine.getInstance();

Object instance=de.javaCodeToObject(fullName,src.toString());

System.out.println(instance);

}

}

/Users/yangyu/Downloads/myclass文件(这里使用文件,实际也可以在程序中直接拼凑String)

packagecom.seeyon.proxy;public classMyClass {publicString say(String str){return "hello"+str;

}

}

最后

以上就是痴情羊为你收集整理的自定义 编译 java_Java--自定义Class并且在内存中编译,加载,实例化的全部内容,希望文章能够帮你解决自定义 编译 java_Java--自定义Class并且在内存中编译,加载,实例化所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部