博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java动态编译
阅读量:4983 次
发布时间:2019-06-12

本文共 5184 字,大约阅读时间需要 17 分钟。

程序产生过程

下图展示了从源代码到可运行程序的过程,正常情况下先编译(明文源码到字节码),后执行(JVM加载字节码,获得类模板,实例化,方法使用)。本文来探索下当程序已经开始执行,但在.class甚至.java还未就绪的情况下,程序如何获得指定的实现。这就是我们下面的主题,动态编译。

程序流程图

相关类介绍

JavaCompiler: 负责读取源代码,编译诊断,输出class

JavaFileObject: 文件抽象,代表源代码或者编译后的class
JavaFileManager: 管理JavaFileObject,负责JavaFileObject的创建和保存位置
ClassLoader: 根据字节码,生成类模板

使用方式

由于代码在编译的时候,类定义甚至类名称还不存在,所以没法直接声明使用的。只能定义一个接口代替之,具体实现留给后面的动态编译。

public interface Printer {    public void print();}

源代码的文件级动态编译

java源码以文件的形式存在本地,程序去指定路径加载源文件。

String classPath = File2Class.class.getResource("/").getPath();//在这里我们是动态生成定义,然后写入文件。也可以直接读一个已经存在的文件String str = "import classloader.Printer;"     + "public class MyPrinter1 implements Printer {"     + "public void print() {"     + "System.out.println(\"test1\");"     + "}}";FileWriter writer = new FileWriter(classPath + "MyPrinter1.java");writer.write(str);;writer.close();//获得系统编译器JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null, null);//读入源文件Iterable fileObject = fileManager.getJavaFileObjects(classPath + "MyPrinter1.java");//编译JavaCompiler.CompilationTask task = compiler.getTask(                null, fileManager, null, null, null, fileObject);task.call();fileManager.close();//指定class路径,默认和源代码路径一致,加载classURLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:" + classPath)});Printer printer = (Printer)classLoader.loadClass("MyPrinter1").newInstance();printer.print();

源代码的内存级动态编译

上节源代码落地了,这节让我们看下源代码和class全程在内存不落地,如何实现动态编译。思路是生成源代码对应的JavaFileObject时,从内存string读取;生成class对应的JavaFileObject时,以字节数组的形式存到内存。JavaFileObject是一个interface, SimpleJavaFileObject是JavaFileObject的一个基本实现,当自定义JavaFileObject时,继承SimpleJavaFileObject,然后改写部分函数。

自定义JavaSourceFromString,作为源代码的抽象文件(来自JDK API文档)

/** * A file object used to represent source coming from a string.*/public class JavaSourceFromString extends SimpleJavaFileObject {/** * The source code of this "file". */final String code;/** * Constructs a new JavaSourceFromString. * @param name the name of the compilation unit represented by this file object * @param code the source code for the compilation unit represented by this file object */JavaSourceFromString(String name, String code) {    super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);    this.code = code;}@Overridepublic CharSequence getCharContent(boolean ignoreEncodingErrors) {    return code;}}

  

JavaClassFileObject,代表class的文件抽象

public class JavaClassFileObject extends SimpleJavaFileObject {    //用于存储class字节    ByteArrayOutputStream outputStream;    public JavaClassFileObject(String className, Kind kind) {        super(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind);        outputStream = new ByteArrayOutputStream();    }    @Override    public OutputStream openOutputStream() throws IOException {        return outputStream;    }    public byte[] getClassBytes() {        return outputStream.toByteArray();    }}

  

ClassFileManager,修改JavaFileManager生成class的JavaFileObject的行为,另外返回一个自定义ClassLoader用于返回内存中的字节码对应的类模板

public class ClassFileManager extends ForwardingJavaFileManager {    private JavaClassFileObject classFileObject;    /**     * Creates a new instance of ForwardingJavaFileManager.     *     * @param fileManager delegate to this file manager     */    protected ClassFileManager(JavaFileManager fileManager) {        super(fileManager);    }    /**     * Gets a JavaFileObject file object for output     * representing the specified class of the specified kind in the given location.     */    @Override    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,     FileObject sibling) throws IOException {        classFileObject = new JavaClassFileObject(className, kind);        return classFileObject;    }    @Override    //获得一个定制ClassLoader,返回我们保存在内存的类    public ClassLoader getClassLoader(Location location) {        return new ClassLoader() {            @Override            protected Class
findClass(String name) throws ClassNotFoundException { byte[] classBytes = classFileObject.getClassBytes(); return super.defineClass(name, classBytes, 0, classBytes.length); } }; }}

  

下面来偷梁换柱,用自定义的JavaFileObject/JavaFileManager来动态编译

String str = "import Printer;"     + "public class MyPrinter2 implements Printer {"     + "public void print() {"    + "System.out.println(\"test2\");"    + "}}";//生成源代码的JavaFileObjectSimpleJavaFileObject fileObject = new JavaSourceFromString("MyPrinter2", str);JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//被修改后的JavaFileManagerJavaFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));//执行编译JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, Arrays.asList(fileObject));task.call();//获得ClassLoader,加载class文件ClassLoader classLoader = fileManager.getClassLoader(null);Class printerClass = classLoader.loadClass("MyPrinter2");//获得实例Printer printer = (Printer) printerClass.newInstance();printer.print();

  

参考

 

转载于:https://www.cnblogs.com/whuqin/p/4981948.html

你可能感兴趣的文章
[Gatsby] Install Gatsby and Scaffold a Blog
查看>>
[Recompose] Add Local State to a Functional Stateless Component using Recompose
查看>>
Spring Boot + Spring Data + Elasticsearch实例
查看>>
我的机器学习之旅(一):认识机器学习
查看>>
util包下Timer类的延迟执行
查看>>
缓冲区溢出漏洞实验
查看>>
失业的程序员(十):分歧的产生
查看>>
[FZU2261]浪里个浪
查看>>
四则运算*2
查看>>
《Linux就该这么学》 - 必读的红帽系统与红帽linux认证自学手册
查看>>
名句名篇
查看>>
图像的基本运算——scale, rotation, translation
查看>>
OpenCV——PS滤镜, 碎片特效
查看>>
python-字典相关函数认识
查看>>
Java之IO流
查看>>
Lua学习笔记-C API
查看>>
浅析:Android 嵌套滑动机制(NestedScrolling)
查看>>
Python+Selenium练习篇之18-获取元素上面的文字
查看>>
php状态模式
查看>>
Asp.net C# 图像处理
查看>>