之前由组里的其它同学结合 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>
...
初看起来,步骤大概是这样的:
mvn package
打包。具体打包方法使用了 assembly 插件来定义。mvn deploy:deploy-file
进行构建后 jar 包的上传。初步来看,有几个点比较在意
mvn deploy
上传到仓库的,mvn deploy:deploy-file
又是什么?从这两个方向,开始进行问题的探究。
通过查看 官方使用文档 (网页有一个库来自 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 clean
、mvn install
以及 Surefire
插件有更清晰的定位。
文中大多为自己探究的结果,可能有所偏颇。如果大家有更好的想法,也欢迎回帖交流~