通用技术 记一次 maven 上传公司私有库问题的解决过程

陈恒捷 for PPmoney · 2018年07月03日 · 最后由 陈恒捷 回复于 2018年07月03日 · 2884 次阅读

之前由组里的其它同学结合 gitlab pipeline 做了一个自动上传框架到私有库的功能,但近期使用发现存在一些问题,例如无法通过 exclude 去除其中的部分依赖引入,因此需要去具体探究 maven 上传公司私有库的过程,进而解决问题。

问题描述

最近收到一些反馈,内部框架的依赖包比较大(100 多 M),而且遇到依赖冲突时,无法通过 exclude 去除部分依赖。

经过查看,框架上传到私有库的代码大致如下:

# variables
URL=公司私有库地址
RID=公司私有库别名(与 maven settings.xml 中 <server> 节点的 id 字段保持一致)

# build
mvn -p package

# upload
jar=通过正则获取 target/ 下的 jar 包相对路径
version=通过正则获取 pom.xml 中的版本号
mvn deploy:deploy-file -DgroupId=组名 -DartifactId=框架名 -Dfile=$jar -Dversion=$version -DrepositoryId=$RID -Durl=$URL

同时,pom.xml 文件中有用到 assembly 插件辅助打包:

...
            <plugin>
                   <artifactId> maven-assembly-plugin </artifactId>
                   <configuration>
                        <descriptorRefs>
                             <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <archive>
                             <manifest>
                                  <mainClass>com.XX.main</mainClass>
                             </manifest>
                        </archive>
                   </configuration>
                   <executions>
                        <execution>
                             <id>make-assembly</id>
                             <phase>package</phase>
                             <goals>
                                  <goal>single</goal>
                             </goals>
                        </execution>
                   </executions>
              </plugin>

...

初看起来,步骤大概是这样的:

  1. mvn package 打包。具体打包方法使用了 assembly 插件来定义。
  2. 通过正则表达式,获取到构建后 jar 包的路径以及版本号。
  3. 通过 mvn deploy:deploy-file 进行构建后 jar 包的上传。

初步来看,有几个点比较在意

  1. assembly 插件具体是怎么打的包?
  2. 平时看到开发都是直接 mvn deploy 上传到仓库的,mvn deploy:deploy-file 又是什么?

从这两个方向,开始进行问题的探究。

assembly 插件解析

通过查看 官方使用文档 (网页有一个库来自 google ,需要科学上网才能打开。。。)、官方关于 descriptorRef 的说明文档,大致了解了 assembly 插件的初衷和大致玩法。

assembly 插件,初衷是用于把项目的依赖项、模块、文档等资料,都统一汇总组装到一个发布包中。目的是为了这个发布包可以在其它机器上直接运行,减少对环境的依赖。针对组装方式,可以通过 <descriptorRefs> 使用内置的组装方式,也可以使用 <descriptors> 结合代码来自定义组装方式。

也根据官方文档,基本了解了 pom.xml 中 assemble 插件几个重要内容的含义:

<plugin>
       <artifactId> maven-assembly-plugin </artifactId>
       <configuration>
            <descriptorRefs>
                <!--使用 jar-with-dependencies 方式打包,即把这个应用的所有二进制输出,包括依赖包的,都一起打包到 jar 文件中-->
                 <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
            <archive>
                <!--指定 main 类的位置,用作直接通过 java -jar 调用的可执行文件时的入口-->
                 <manifest>
                      <mainClass>com.XX.main</mainClass>
                 </manifest>
            </archive>
       </configuration>
       <executions>
            <!--绑定到达 mvn package 这个阶段时,执行这个插件本身的 single 方式-->
            <execution>
                 <id>make-assembly</id>
                 <phase>package</phase>
                 <goals>
                      <goal>single</goal>
                 </goals>
            </execution>
       </executions>
  </plugin>

但因为框架发布,其实只要求发布到 maven 远程仓库,并不需要打包成完全独立的 jar 包供用户单独使用。因此,其实并不需要用上 assembly 插件。而且用上了 assembly 插件后,依赖是直接打进了 jar 包,而非期望的通过依赖的方式引入,导致无法通过 exclude 来去除部分依赖。

结论:应该直接去掉 assembly 插件部分,改为使用 maven 本身自带的打包方式。

mvn deploy:deploy-file 探究

通过查看官方文档,对 deploy 相关命令的说明如下:

  • deploy:deploy: 用于自动安装这个 artifact 、pom 以及多项目中的附属 artifact 。大部分的部署相关信息都会直接使用项目的 pom 文件中的信息。
  • deploy:deploy-file: 用于安装一个独立的 artifact 以及它的 pom 文件。在有需要的时候。artifact 的部署信息可以从另外一个特定的 pom 文件中读取,并通过命令行改写/补充里面的信息。

简单的说,deploy:deploy 设计目的是用于以最简便的方式部署 maven 项目。deploy:deploy-file 则是通过命令行或者外部 pom 文件自定义上传一个 artifact ,更适用于本身通过非 maven 项目打出来的包。

由此看来,使用 deploy:deploy-file 并非正确选择,用 deploy:deploy 即可。也和当时的小伙伴沟通过为何要使用 deploy:deploy-file ,答复是当时试验过各种方式,只有这个生效,所以就保留了这个了。至于 deploy:deploy 为何不生效,他已经不大记得了。

结论:应该把上传命令从 deploy:deploy-file 改为 deploy:deploy ,让其自动附带上 pom 文件,用户使用时会自动读取 pom 文件引入其它依赖。

小结

后续经过试验,去掉了 assembly 插件和改用 deploy:deploy 后,无法 exclude 的问题已经得到解决,而且框架打出来的 jar 包因为不再需要包含依赖,也成功从 100M+ 瘦身到了 1M 内,加快了加载速度。

整体 gitlab 文件改为如下:

# upload
mvn deploy:deploy

同时 pom 文件去掉 assembly 插件部分的内容,参考 https://blog.csdn.net/kang389110772/article/details/72903478, 补充 maven 仓库相关信息:

<distributionManagement>
    <repository>
      <id>公司私有库别名</id>
      <name>MyCo Internal Repository</name>
      <url>Host to Company Repository</url>
    </repository>
    <snapshotRepository>
        <id>公司私有库snapshots别名</id>
        <name>libs-snapshots</name>
        <url>Host to Snapshot</url>
    </snapshotRepository>
</distributionManagement>

settings.xml 运维已在服务器配置好,故不需要再特意配置。

反思

从这次探究中,采用了先探究每个步骤命令的作用,再逐个击破的方式,让自己更有目标,避免直接看官方文档看晕的问题。而且也借此机会对我们平常使用的 maven 有了更结构化的了解。特别推荐大家看下官方的 plugin 列表生命周期,能对自己日常用的 mvn cleanmvn install 以及 Surefire 插件有更清晰的定位。

文中大多为自己探究的结果,可能有所偏颇。如果大家有更好的想法,也欢迎回帖交流~

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

呃...好几个deply?是不是少了个o?一般我们的使用场景是assembly用来打可执行 jar,deploy用到直接上传 jar,deploy-file通常用于直接上传打好的 jar,deploy是 maven 生命周期中的一环,在执行之间默认会编译打包,所以不用手工打,使用方法只需要在 pom 文件里配置distributionManagement,相比deploy-file要方便一些。

AngryTester 回复

谢谢指正,deploy 拼写已更正。

这三者的区别确实如你所说,总结得很清晰。distributionManagement 这个遗漏了说明,我这两天补充一下。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册