概述
本文的目的:
使用者在程序运行期间,可以动态的写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并且在内存中编译,加载,实例化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复