java代码方式对源码进行编译并打包成jar

相信有一些小伙伴会遇到这样的需求:我们希望针对一部分引用定制化jar包的java文件,通过后台代码的方式,将java源文件编译之后并打包成jar包,并最终返回jar包所在的目录,通过FileInputStream的方式,在配置前台就可以将此jar包导出!

在某些特定场景:比如能力开放平台访问SDK,或者定制化RPC框架的客户端SDK,如果我们可以将此客户端远程调用的相关配置文件(比如zk地址,或者token认证代码),一并以数据库的形式配置到前台界面,然后通过此sdk下载的模式,可以快速实现我平台系统和第三方厂家的快速接入使用!

话不多说,下面就上代码!

本组件主要包含三大部分,首先是主方法:


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.tools.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 将源码编译后生成jar包
 * @author wenyn
 * @since 2020-05-18
 */
public class BmgJarPackageUtil {

    private static final Log log = LogFactory.getLog(BmgJarPackageUtil.class);

    // 源码路径
    private static String source = ApplicationConfig.getVal("bmg.compile.source");

    // 打jar包的路径
    private static String bin = ApplicationConfig.getVal("bmg.compile.bin");

    // 依赖包路径
    private static String lib = ApplicationConfig.getVal("bmg.compile.lib");

    /**
     * 判断字符串是否为空 有值为true 空为:false
     */
    private static boolean isnull(String str) {
        if (null == str) {
            return false;
        } else if ("".equals(str)) {
            return false;
        } else if (str.equals("null")) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 编译java文件
     *
     * @param encoding    编译编码
     * @param jars        需要加载的jar
     * @param filePath    文件或者目录(若为目录,自动递归编译)
     * @param sourceDir   java源文件存放目录
     * @param targetDir   编译后class类文件存放目录
     * @param diagnostics 存放编译过程中的错误信息
     * @return
     * @throws Exception
     */
    private static boolean compiler(String encoding, String jars, String filePath, String sourceDir, String targetDir, DiagnosticCollector<JavaFileObject> diagnostics, String sign)
            throws Exception {
        // 获取编译器实例
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 获取标准文件管理器实例
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        try {
            if (!isnull(filePath) && !isnull(sourceDir) && !isnull(targetDir)) {
                return false;
            }
            // 得到filePath目录下的所有java源文件
            File sourceFile = new File(filePath);
            List<File> sourceFileList = new ArrayList<File>();
            getSourceFiles(sourceFile, sourceFileList, targetDir, sign);
            // 没有java文件,直接返回
            if (sourceFileList.size() == 0) {
                System.out.println(filePath + "目录下查找不到任何java文件");
                return false;
            }
            // 获取要编译的编译单元
            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList);
            /**
             * 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录。
             */
            Iterable<String> options = Arrays.asList("-encoding", encoding, "-classpath", jars, "-d", targetDir, "-sourcepath", sourceDir);
            JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
            // 运行编译任务
            return compilationTask.call();
        } finally {
            fileManager.close();
        }
    }

    /**
     * 查找该目录下的所有的java文件
     *
     * @param sourceFile
     * @param sourceFileList
     * @throws Exception
     */
    private static void getSourceFiles(File sourceFile, List<File> sourceFileList, String targetDir, String sign) throws Exception {
        if (sourceFile.exists() && sourceFileList != null) {//文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            try {
                                String cpSrc = pathname.getPath();
                                String newTarget = targetDir.substring(0, targetDir.lastIndexOf(sign)-1);
                                String cpTarget = newTarget + pathname.getPath().substring(pathname.getPath().indexOf("src") + 3, pathname.getPath().length());

                                new CopyDirectory().copyDirectiory(cpSrc, cpTarget);
                            } catch (IOException e) {
                                log.error("BmgJarPackageUtil.getSourceFiles--{}文件夹拷贝出现异常...", e);
                                return false;
                            }
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".java") ? true : false) {
                                return true;
                            }
                            try {
                                String newTarget = targetDir.substring(0, targetDir.lastIndexOf(sign)-1);
                                String cpTarget = newTarget + pathname.getPath().substring(pathname.getPath().indexOf("src") + 3, pathname.getPath().length());
                                new CopyDirectory().copyFile(pathname, new File(cpTarget));
                            } catch (IOException e) {
                                log.error("BmgJarPackageUtil.getSourceFiles--{}文件拷贝出现异常...", e);
                                return false;
                            }
                            return false;
                        }
                    }
                });
                // 递归调用
                for (File childFile : childrenFiles) {
                    getSourceFiles(childFile, sourceFileList, targetDir, sign);
                }
            } else {// 若file对象为文件
                sourceFileList.add(sourceFile);
            }
        }
    }

    /**
     * 查找该目录下的所有的jar文件
     *
     * @param jarPath
     * @throws Exception
     */
    private static String getJarFiles(String jarPath) throws Exception {

        StringBuilder jarsBuilder = new StringBuilder();

        File sourceFile = new File(jarPath);
        if (sourceFile.exists()) {// 文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".jar") ? true : false) {
                                jarsBuilder.append(pathname.getPath()).append(";");
                                return true;
                            }
                            return false;
                        }
                    }
                });
            }
        }
        return jarsBuilder.toString();
    }

    /**
     * 获取所有class文件,并将其变为Class对象
     * @param jarFileCreator
     * @param rootDir
     * @param targetDir
     */
    private static void getClass(JarFileCreator jarFileCreator, String rootDir, String targetDir){

        File classDir = new File(targetDir);

        if(classDir.isDirectory()){

            File[] files = classDir.listFiles();

            for(File file :files){

                getClass(jarFileCreator, rootDir, file.getPath());
            }
        } else {

            String name = classDir.getName();

            // class文件才可以加入编译路径
            if(name.endsWith(".class")){
                ThirdClassLoader thirdClassLoader = new ThirdClassLoader(rootDir);
                Class clazz = thirdClassLoader.findClass(targetDir);
                jarFileCreator.addClass(clazz, targetDir);
            }
        }
    }

    /**
     * 将class文件打包成jar包
     * @param jarPath
     * @param bin
     * @throws Exception
     */
    private static void writeNewJar(String jarPath, String bin) throws Exception{

        JarFileCreator jarFileCreator = new JarFileCreator(new File(jarPath));

        getClass(jarFileCreator, bin, bin);

        jarFileCreator.createJarFile();
    }

    /**
     * 将源码打包成jar
     * @param sign
     * @param jarName
     * @throws Exception
     */
    public static String packageJar(String sign, String jarName) throws Exception{
        try {
            String newSource = source + File.separator + sign;
            String newBin = bin + File.separator + sign;
            String newJarPath = newBin + File.separator + jarName;

            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

            boolean compilerResult = compiler("UTF-8", getJarFiles(lib), newSource, newSource, newBin, diagnostics, sign);

            if (!compilerResult) {
                log.error("BmgJarPackageUtil.compile--{}源码编译失败...");
                for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                    log.error("BmgJarPackageUtil.compile--{}" + diagnostic.getMessage(null));
                }
            } else {
                log.info("BmgJarPackageUtil.compile--{}源码编译成功,准备打包..");
                writeNewJar(newJarPath, newBin);

                // 最终返回新生成的jar包路径
                return newJarPath;
            }

        } catch (Exception e) {
            log.error("BmgJarPackageUtil.compile--{}源码编译出现异常..", e);
        }

        return null;
    }

    public static void main(String[] args) {
        try {
            // 生成源码的时候,源码路径必须具备唯一性,防止多个请求同时过来,不同源码之间相互影响
            String path = packageJar("222", "test333.jar");
            System.out.println(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此主类主要作用有三点:

1)获取依赖的Jar包--getJarFiles

2)获取所有源码类并复制到目标目录--getSourceFiles

3)在目标目录编译源码--compiler

4)在目标目录获取所有class文件--getClass

6)生成jar包--writeNewJar

 

网上大多数提供的打jar包,要么是依赖ant,要么就是依赖maven插件,少数几个通过java代码来编译的,也没结合打jar包过程,而且还仅仅只能打那种只有jdk依赖的工程,功能比较单一。

我在网上找了不少相关代码,做了整体的梳理和重新整理,并形成目前比较有效的工具代码,并在平台上分享出来,多谢浏览!

另外,整体代码,由于我今天刚写博客,还未审核通过,等审核通过,我会贴上链接地址!

文中用到的相关文献如下所示:

http://www.xwood.net/_site_domain_/_root/5870/5874/t_c274926.html

https://blog.csdn.net/xiaozaq/article/details/54350239

https://blog.csdn.net/qq_20641565/article/details/78744677

https://www.cnblogs.com/yang3wei/archive/2012/04/04/2739801.html

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值