前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java动态编译实现

java动态编译实现

作者头像
执笔记忆的空白
发布2020-12-24 10:17:02
1.7K1
发布2020-12-24 10:17:02
举报
文章被收录于专栏:Java日常

0 概述

本文主要实现java code 动态编译,并使用自定义的ClassLoader加载动态编译生成的字节码。

1 代码

DynamicCompile 类主负责调用JDK API 实现动态编译以及使用ClassLoader加载编译后生成的字节码。

代码语言:javascript
复制
import javax.tools.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class DynamicCompile {
    private URLClassLoader parentClassLoader;
    private String classpath;
    public DynamicCompile() {
        this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();
        this.buildClassPath();// 存在动态安装的问题,需要动态编译类路径
    }

    private void buildClassPath() {
        this.classpath = null;
        StringBuilder sb = new StringBuilder();
        for (URL url : this.parentClassLoader.getURLs()) {
            String p = url.getFile();
            sb.append(p).append(File.pathSeparator); //路径分割符linux为:window系统为;
        }
        this.classpath = sb.toString();
    }
    /**
     * 编译出类
     *
     * @param fullClassName 全路径的类名
     * @param javaCode      java代码
     *
     * @return 目标类
     */
    public Class<?> compileToClass(String fullClassName, String javaCode) throws Exception {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));

        List<JavaFileObject> jfiles = new ArrayList<>();
        jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode));

        List<String> 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);
            //加载至内存
            return dynamicClassLoader.loadClass(fullClassName, jco);
        } else {
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                String error = compileError(diagnostic);
                throw new RuntimeException(error);
            }
            throw new RuntimeException("compile error");
        }
    }

    private String compileError(Diagnostic diagnostic) {
        StringBuilder res = new StringBuilder();
        res.append("LineNumber:[").append(diagnostic.getLineNumber()).append("]\n");
        res.append("ColumnNumber:[").append(diagnostic.getColumnNumber()).append("]\n");
        res.append("Message:[").append(diagnostic.getMessage(null)).append("]\n");
        return res.toString();
    }
}

CharSequenceJavaFileObject 用于包装原始的java code 。

代码语言:javascript
复制
import javax.tools.SimpleJavaFileObject;
import java.net.URI;


public class CharSequenceJavaFileObject extends SimpleJavaFileObject {

    private CharSequence code;

    public CharSequenceJavaFileObject(String className, CharSequence code) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

JavaClassObject 存放编译后结果

代码语言:javascript
复制
import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;


public class JavaClassObject extends SimpleJavaFileObject {

    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

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

    public byte[] getBytes() {
        return outputStream.toByteArray();
    }

    //编译时候会调用openOutputStream获取输出流,并写数据
    @Override
    public OutputStream openOutputStream() throws IOException {
        return outputStream;
    }
}

文件管理类

代码语言:javascript
复制
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.io.IOException;



public class ClassFileManager extends ForwardingJavaFileManager {
    private JavaClassObject jclassObject;

    public ClassFileManager(StandardJavaFileManager standardManager) {
        super(standardManager);
    }

    public JavaClassObject getJavaClassObject() {
        return jclassObject;
    }

    //需要覆盖
    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,
                                               FileObject sibling) throws IOException {
        jclassObject = new JavaClassObject(className, kind);
        return jclassObject;
    }
}

classLoader 加载编译生成的字节码

代码语言:javascript
复制
public class DynamicClassLoader extends ClassLoader {

    public DynamicClassLoader(ClassLoader parent) {
        super(parent);
    }
    public Class loadClass(String fullName, JavaClassObject jco) {
        byte[] classData = jco.getBytes();
        return this.defineClass(fullName, classData, 0, classData.length);
    }
}

2 测试

代码语言:javascript
复制
public class Main {

    public static void main(String[] args) throws Exception {
        DynamicCompile dynamicCompile = new DynamicCompile();
        String code = "package com.hsc.study.com.hsc.test;\n" +
                "\n" +
                "/**\n" +
                " * Created by yufei on 17/5/13.\n" +
                " */\n" +
                "public class Test {\n" +
                "    @Override\n" +
                "    public String toString() {\n" +
                "        return \"test\"\n" + ";" +
                "    }\n" +
                "}\n";
        Class<?> classz = dynamicCompile.compileToClass("com.hsc.study.Test", code);
        System.out.println(classz.newInstance());
    }
}
代码语言:javascript
复制
public class Test {
    @Override
    public String toString() {
        return "test";
    }
}

输出:test

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/06/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 概述
  • 1 代码
  • 2 测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档