java动态编译及生成jar包

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

/**
 * @author xm
 * @version 1.0
 * @date 2022/7/22 19:45
 */
@Slf4j
public class JavaUils {
    private JavaUils() {
    }

    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream error = JavaUils.compileFile(javaList, compileExtJarNameSet);
        if (error != null) {
            log.error("compileFile fail. -->" + error);
        }
        // 导出jar包
        File file = JavaUils.exportJar("jarName.jar");
        JavaUils.deleteTempFile();
    }

    /**
     * 编译文件生成路径
     */
    private static final String COMPILE_JAVA_FILE_PATH = "." + File.separator + "compile_java_file" + File.separator;
    private static final String COMPILE_LIB = "compile_lib/";
    private static final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
    public static final String JAVA_SUFFIX = ".java";

    static {
        log.info("---------javaCompiler------" + javaCompiler);
        if (javaCompiler == null) {
            // 本地cmd运行时ToolProvider.getSystemJavaCompiler()会获取不到javaCompiler,可以使用JavacTool.create()来创建
            log.error("ToolProvider.getSystemJavaCompiler() is null.");
        }
    }

    public static String packagePath(String fullPathClassName) {
        if (fullPathClassName.endsWith(JAVA_SUFFIX)) {
            return fullPathClassName.substring(0, fullPathClassName.lastIndexOf("."))
                    .substring(0, fullPathClassName.lastIndexOf("."))
                    .replace(".", File.separator) + File.separator;
        }
        return fullPathClassName.substring(0, fullPathClassName.lastIndexOf("."))
                .replace(".", File.separator) + File.separator;
    }

    /**
     * @param fullPathClassName com..dto.order.TestReq
     * @return
     */
    public static String fileName(String fullPathClassName) {
        if (fullPathClassName.endsWith(JAVA_SUFFIX)) {
            return fullPathClassName.substring(0, fullPathClassName.length() - JAVA_SUFFIX.length())
                    .substring(fullPathClassName.lastIndexOf(".") + 1) + JAVA_SUFFIX;
        }
        return fullPathClassName.substring(fullPathClassName.lastIndexOf(".") + 1) + JAVA_SUFFIX;
    }

    public static void validatedCompileFile(String fullPathClassName, String javaStr) {
        int lastIndex = fullPathClassName.lastIndexOf(".");
        if (lastIndex > 0) {
            String packagePath = fullPathClassName.substring(0, lastIndex);
            if (!javaStr.contains(packagePath)) {
                throw new CompilationException("validated packagePath fail");
            }
            String className = fullPathClassName.substring(lastIndex + 1);
            if (!javaStr.contains(className)) {
                throw new CompilationException("validated className fail");
            }
        }
    }

    /**
     * 编译指定的所有.java文件,编译后的文件
     * @param collect 需要包含待编译的类文件.java及类的全路径类名
     * @param copyJarNameSet 编译需要用到的第三方依赖jar。会去到springboot打包时默认的lib下获取,如需手动指定需要调整代码
     * @return 为null时表示成功;非null时包含编译出错时的错误信息,可直接转为字符串打印
     */
    public static ByteArrayOutputStream compileFile(Collection<JavaCompilerInfo> collect, Set<String> copyJarNameSet) throws IOException {
        String loadJarParam = getLoadJarParam(copyJarNameSet);
        String[] strings;
        int i = 2;
        if (loadJarParam != null && !loadJarParam.isEmpty()) {
            strings = new String[collect.size() + 4];
            strings[2] = "-classpath";
            strings[3] = loadJarParam;
            i = 4;
        }else {
            strings = new String[collect.size() + 2];
        }
        // 设置编码,避免编码问题
        strings[0] = "-encoding";
        strings[1] = "utf-8";
        for (JavaCompilerInfo info : collect) {
            File file = new File(COMPILE_JAVA_FILE_PATH + JavaUils.packagePath(info.getFullPathClassName()));
            if (!file.exists()) {
                file.mkdirs();
            }
            file = new File(file.getPath() + File.separator + JavaUils.fileName(info.getFullPathClassName()));
            file.createNewFile();
            FileUtils.write(file, info.getJavaStr(), StandardCharsets.UTF_8);
            strings[i] = file.getPath();
            i++;
        }
        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        int result = javaCompiler.run(null, null, byteOutputStream, strings);
        if (result == 0) {
            return null;
        }
        return byteOutputStream;
    }

    private static String getLoadJarParam(Set<String> copyJarNameSet) throws IOException {
        if (copyJarNameSet == null || copyJarNameSet.isEmpty()) {
            return null;
        }
        JarFile jarFile = getJarFile();
        if (jarFile == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        for (String jarName : copyJarNameSet) {
            if (jarName.trim().isEmpty()) {
                continue;
            }
            String fileAbsolutePath = copyJarToCompileLib(jarFile, jarName);
            if (fileAbsolutePath != null) {
                builder.append(fileAbsolutePath);
            }
        }
        String optionStr = builder.toString();
        log.info("compiler option -classpath:{}", optionStr);
        return optionStr;
    }

    private static String copyJarToCompileLib(JarFile jarFile , String jarName) {
        try {
            File destination = new File(COMPILE_LIB + jarName);
            if (destination.exists() && destination.length() > 0) {
                log.info("copyJarToCompileLib the jar file already exists. file.length():" + destination.length());
                return destination.getAbsolutePath() + File.pathSeparator;
            }
            JarEntry jarEntry = jarFile.getJarEntry("BOOT-INF/lib/" + jarName);
            if (jarEntry == null) {
                log.error("jar file not found. jarName:{}", jarName);
                return null;
            }
            InputStream inputStream = jarFile.getInputStream(jarEntry);
            FileUtils.copyInputStreamToFile(inputStream, destination);
            log.info("copyJarToCompileLib success. jarName:{}", jarName);
            return destination.getAbsolutePath() + File.pathSeparator;
        } catch (Exception e) {
            log.error("copyJarToCompileLib fail. jarName:{}", jarName, e);
        }
        return null;
    }

    private static JarFile getJarFile() throws IOException {
        String projectJarName = "springboot-test.jar";
        File file = new File(projectJarName);
        if (file.exists()) {
            return new JarFile(file);
        }
        File file2 = new File("lib" + File.separator + projectJarName);
        if (file2.exists()) {
            return new JarFile(file2);
        }
        log.error("projectJar:{} not found", projectJarName);
        return null;
    }

    public static boolean compileFile(String javaStr) {
        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        int result = javaCompiler.run(null, null, byteOutputStream, javaStr);
        if (result == 0) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
    private static File getOutFile(String outPath) throws IOException {
        File file = new File(outPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        return file;
    }

    public static File exportJar(String outPath) throws IOException {
        return exportJar(COMPILE_JAVA_FILE_PATH, outPath);
    }

    public static File exportJar(String packagePath, String outPath) throws IOException {
        File outFile = getOutFile(outPath);
        JarOutputStream out = new JarOutputStream(new FileOutputStream(outFile), getManifest());
        createTempJarInner(out, new File(packagePath), "");
        out.flush();
        out.close();
        return outFile;
    }

    /**
     * @param out
     * @param f    绝对路径 G:\git\compile\xm-compile\src\test\java\com\dto\OrderDto.java
     * @param base 导出jar包里面此文件的路径
     * @throws IOException
     */
    private static void createTempJarInner(JarOutputStream out, File f, String base) throws IOException {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            if (fl != null && fl.length > 0) {
                if (base.length() > 0) {
                    base = base + "/";
                }
                for (File file : fl) {
                    createTempJarInner(out, file, base + file.getName());
                }
            }
        } else {
            if (base.endsWith(".class")) {
                toJar(out, f, base);
            }
        }
    }

    public static void deleteTempFile() throws IOException {
        FileUtils.forceDelete(new File(COMPILE_JAVA_FILE_PATH));
    }

    private static void toJar(JarOutputStream out, File f, String base) throws IOException {
        log.info("export to jar -->" + base);
        out.putNextEntry(new JarEntry(base));
        try(FileInputStream in = new FileInputStream(f)) {
            byte[] buffer = new byte[1024];
            int n = in.read(buffer);
            while (n != -1) {
                out.write(buffer, 0, n);
                n = in.read(buffer);
            }
        }
    }

    private static Manifest getManifest() {
        Manifest manifest = new Manifest();
        manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
        //manifest.getMainAttributes().putValue("Main-Class", "Show");//指定Main Class
        return manifest;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值