cover

Java 集成Maven Embedder 编译 Maven 项目

背景

最近在开发代码覆盖率的平台,涉及到对 java 项目编译后使用 jacococli 来生成报告,需要指定 java 项目 class 文件路径和源码路径。因此,需要对用户配置的 java 项目在服务器上进行编译,Java 项目构建工具有很多种,比如 Ant,Maven,Gradle 等,本文只针对 Maven 构建的项目如何编译进行讲解。以下用于演示编译的项目是 apolloconfig/apollo

编译方案

Maven 项目编译其实很简单,常用的命令就是

mvn clean package

但是我在上面说了,我是要在部署了代码覆盖率服务(以下简称 cov 服务,使用 springboot 开发)的服务器上进行编译,这个 cov 服务本身就是使用 Maven 构建,然后使用 Dockerfile 打包成镜像,在 k8s 上部署的,在这个镜像里面只有必要的 JDK 依赖 。因此,如果要在 cov 服务上进行 Maven 项目的构建,则需要先配置 Maven 环境,因为这个镜像是由运维统一提供,所以再往镜像里面打包 Maven 这个事情推动起来比较困难(不要问我哪里困难,问就是他们不愿意),因此,需要使用其他方式来创建 Maven 项目的编译环境。

使用 Maven 工具编译

使用 Maven 工具编译应该是绝大多数人的第一反应,直接在项目启动时从 Maven 官方下载一个apache-maven-3.8.8-bin.zip安装包,解压到指定目录,然后通过 Java Runtime 来执行命令即可。道理确实如此,所以可以使用如下脚本进行安装。

wget https://dlcdn.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.zip && unzip apache-maven-3.8.8-bin.zip -d /opt

项目编译可以使用如下 Java 代码:

Runtime runtime = Runtime.getRuntime();
runtime.exec("cd /Users/wick/Downloads/repo/apollo && mvn -T 4C clean install -Dmaven.test.skip=true");

使用 Maven Embedder 编译

除了上面的方式之外,还有一种方法,就是使用 Maven Embedder 来编译 Maven 项目,这样只需要在我们 cov 服务中集成相关的依赖,就可以直接使用 Java 代码来编译 Maven 项目,无需在服务器上安装 Maven 工具。具体做法如下

  • 在 cov 服务中配置 Maven 依赖
<!-- 省略了其他依赖 -->
<dependencies>
    <dependency>
        <groupId>com.networknt</groupId>
        <artifactId>slf4j-logback</artifactId>
        <version>2.1.25</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-embedder</artifactId>
        <version>3.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-compat</artifactId>
        <version>3.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven.resolver</groupId>
        <artifactId>maven-resolver-connector-basic</artifactId>
        <version>1.9.15</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven.resolver</groupId>
        <artifactId>maven-resolver-transport-http</artifactId>
        <version>1.9.15</version>
    </dependency>
<!-- 拉取代码 -->
    <dependency>
        <groupId>org.eclipse.jgit</groupId>
        <artifactId>org.eclipse.jgit</artifactId>
        <version>6.6.0.202305301015-r</version>
    </dependency>
</dependencies>

  • 代码实现
import org.apache.maven.cli.MavenCli;

/**
 * @author: wick
 * @date: 2023/8/19 21:39
 * @description: Maven编译服务
 */

public class MavenCompilerService {
    public void compiler(String codePath, String... commands) {
        MavenCli cli = new MavenCli();
        String mvnHome = MavenCli.USER_MAVEN_CONFIGURATION_HOME.getAbsolutePath();
        System.getProperties().setProperty("maven.multiModuleProjectDirectory", mvnHome);
        int statusCode = cli.doMain(commands, codePath, System.out, System.err);
        if (statusCode != 0) {
            throw new RuntimeException("编译失败");
        }
    }

    public static void main(String[] args) {
        MavenCompilerService mavenCompilerService = new MavenCompilerService();
        String[] commands = {"-T 4C","clean", "install", "-Dmaven.test.skip=true"};
        mavenCompilerService.compiler("/Users/wick/Downloads/repo/apollo", commands);
    }
}

在上面的 Maven 命令中,使用了一个参数 -T 4C ,作用是:使用 4 核编译,加快编译速度。

执行 main 方法之后,看一下控制台输出:

iShot_2023-08-19_21.44.50.jpg

可以发现这个跟我们正常执行编译命令的输出没有区别,再看一下是不是真的编译完了

编译前 iShot_2023-08-19_21.42.33.jpg

编译后 iShot_2023-08-19_21.44.04.jpg

可以看到,这个项目的 target 目录已经生成,说明项目编译完成。

总结

上面两种编译方案各有优势,直接使用 Maven 工具正常情况下来说是非常方便的,但是缺点是在我现在的场景下不太适用。第二种使用 Maven Embedder 虽然解决了安装工具的问题,但是对于需要执行一些特殊命令,如 deploy 时,就需要开发者去了解这个依赖相关的配置,要知道怎么在 MavenCli 中配置 Maven 仓库推送依赖的账号和密码,以及怎么指定仓库等等问题。所幸,cov 服务只是涉及代码拉取和编译,并不涉及到上述复杂场景,cov 拉取的代码也是公司内部的项目,均是在 pom.xml 中配置好公司内部仓库地址的。等后续遇到其他问题时,再来继续补充此文章内容吧,碎觉!

1
博客已迁移至:https://linvaux.github.io/
加入