如果有个需求是要通过代码调用 maven 命令,来上传 jar 包或进行其他操作,该如何实现?

调用命令行当然可以,maven 官方也有一个项目 Apache Maven Invoker 实现类似需求,但前提是服务运行的机器或者容器中需要有 maven。

大家可能都听说过 Write once, run anywhere. 如果没了 maven 环境就执行不了了,有没有更好的方式呢

Maven Embedder 也是 maven 官方的一个项目,如名字一样,是可嵌入的,也就是不需要依赖外部 maven。项目是个好项目,但问题就是官方提供的文档实在太少了。

以防被喷,这里没有说嵌入一定是更好的方案,毕竟不可能把项目所需的所有依赖都打包到一起,还是要根据实际问题来选择的。

依赖

按照官方其实只需要 maven-embedder 一个依赖就可以,但实际运行起来,会发现报很多错,这里也不啰嗦,直接放出完整可用的依赖。

<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.4.1</version>
</dependency>
<dependency>
    <groupId>org.apache.maven.resolver</groupId>
    <artifactId>maven-resolver-transport-http</artifactId>
    <version>1.4.1</version>
</dependency>

maven-compat 是一个用于保持与 Maven2 兼容性的包。

maven-resolver-connector-basicmaven-resolver-transport-http 用于 deploy。如果只运行到 install,不需要 deploy ,这两个依赖可以去掉。网上很多会用 aether 项目中的依赖,类似下面这种的

 <dependency>
  <groupId>org.eclipse.aether</groupId>
  <artifactId>aether-connector-basic</artifactId>
  <version>1.0.2.v20150114</version>
</dependency>
<dependency>
  <groupId>org.eclipse.aether</groupId>
  <artifactId>aether-transport-wagon</artifactId>
  <version>1.0.2.v20150114</version>
</dependency>

但是实际上 aether 这个项目已经停止了,详见 Aether Termination Review,而 Apache Maven Artifact Resolver 就是替代者。

使用

使用的代码也很简单

public static void deploy(String path){
    MavenCli cli = new MavenCli();
    String mvnHome = MavenCli.USER_MAVEN_CONFIGURATION_HOME.getAbsolutePath();
    System.getProperties().setProperty("maven.multiModuleProjectDirectory", mvnHome);
    List<String> args = new ArrayList<>();
    args.add("clean");
    args.add("deploy");
    int status = 0;
    try {
        status = cli.doMain(args.toArray(new String[]{}), path, System.out, System.out);
    }catch (Exception e){
        logger.error(e.getMessage(),e);
        throw e;
    }
    if (status != 0) {
        throw new RuntimeException("maven 出错");
    }
}

maven.multiModuleProjectDirectory 这个系统变量,如果设置了就无需设置了,但如果没有就需要设置,否则会报错。

另外因为 deploy 时涉及服务器的账户和密码,可能需要配置文件,这里有两种解决方案:

  1. 通过 -s,--settings 来指定配置文件的路径。
  2. 把配置文件放到 maven home 中。

如果使用了密码加密,还涉及 settings-security.xml 文件,这时用第一种就行不太通(主要我没成功),把这两个文件都放到 maven home 是没啥问题的。默认的 maven home 就是用户目录下的.m2目录。

常见错误

-Dmaven.multiModuleProjectDirectory system property is not set.

前面提到了,设置这个系统变量就可以了,值为 maven home 对应的路径即可。

No implementation for org.apache.maven.repository.RepositorySystem was bound.

添加依赖,虽然这是个maven 2 的兼容包,但是还没找到其他的可替代品。

<dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-compat</artifactId>
    <version>3.6.3</version>
</dependency>

No connector factories available、No transporter factories registered

这两个都是部署过程中会报出的错误,添加如下依赖即可解决

<dependency>
    <groupId>org.apache.maven.resolver</groupId>
    <artifactId>maven-resolver-connector-basic</artifactId>
    <version>1.4.1</version>
</dependency>
<dependency>
    <groupId>org.apache.maven.resolver</groupId>
    <artifactId>maven-resolver-transport-http</artifactId>
    <version>1.4.1</version>
</dependency>

码农张思壮
31 声望6 粉丝