2.4 Assembly 交付物打包:把成品打成你要的形状


文档摘要

2.4 Assembly 交付物打包:把成品打成你要的形状 本节摘要:maven-assembly-plugin 通过描述文件自定义交付物的内容与格式,fileSet 收录项目文件、dependencySet 收录依赖,支持 zip 与 tar.gz 等格式。本节完成一次银行环境交付的完整案例,并给出 Assembly 与 Shade 的分工边界。 交付现场的一道选择题 risk-engine 要交付到银行的托管环境:对方不给外网,要求交付物是"一个压缩包,解压即一个目录,里面有程序 jar、全部依赖、启动脚本、版本说明"。 打出的瘦 jar 显然不满足——它只有自己的代码,依赖得靠 classpath 另行拼装。 这就是 Assembly 插件的战场:按你的描述组装任意形状的交付物。

2.4 Assembly 交付物打包:把成品打成你要的形状

本节摘要:maven-assembly-plugin 通过描述文件自定义交付物的内容与格式,fileSet 收录项目文件、dependencySet 收录依赖,支持 zip 与 tar.gz 等格式。本节完成一次银行环境交付的完整案例,并给出 Assembly 与 Shade 的分工边界。

交付现场的一道选择题

risk-engine 要交付到银行的托管环境:对方不给外网,要求交付物是"一个压缩包,解压即一个目录,里面有程序 jar、全部依赖、启动脚本、版本说明"。mvn package 打出的瘦 jar 显然不满足——它只有自己的代码,依赖得靠 classpath 另行拼装。

这就是 Assembly 插件的战场:按你的描述组装任意形状的交付物。先把决策口径立起来,因为很多团队在 Assembly 与 Shade 之间反复纠结:

方案 产物形态 典型场景 主要代价
Assembly(含依赖目录) zip 或 tar.gz:程序 jar 加 lib 目录加脚本 离线交付、运维托管环境 依赖多时文件数量大
Assembly(单文件内嵌) 一个 fat jar 工具类程序、批处理 类冲突叠加 无隔离
Shade 合并后的单个 jar,可改类名避冲突 类库复用、Spark 作业 类合并风险高 排查难
Spring Boot repackage 可执行 jar 内嵌容器 Spring 服务 只适合 Boot 项目

银行场景明显属于第一行:要目录结构、要脚本、要压缩包,Assembly 描述文件正是为此而生。

描述文件:交付物的蓝图

描述文件是 XML,核心元素就两类:fileSet 收录项目内的文件,dependencySet 收录 Maven 解析出的依赖。给 risk-engine 写的第一版:

<assembly> <id>bank-dist</id> <formats> <!-- 产物格式:zip 起文件名后缀的作用 --> <format>zip</format> </formats> <includeBaseDirectory>true</includeBaseDirectory> <fileSets> <!-- 收录项目文件:启动脚本与说明 --> <fileSet> <directory>src/main/assembly/bin</directory> <outputDirectory>bin</outputDirectory> <fileMode>0755</fileMode> <!-- 脚本要可执行 权限位写明 --> </fileSet> <fileSet> <directory>src/main/assembly/docs</directory> <outputDirectory>docs</outputDirectory> </fileSet> </fileSets> <dependencySets> <!-- 收录全部运行期依赖 --> <dependencySet> <outputDirectory>lib</outputDirectory> <scope>runtime</scope> <useProjectArtifact>true</useProjectArtifact> <!-- 程序 jar 自身也进 lib 目录 --> </dependencySet> </dependencySets> </assembly>

POM 里的接线:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.4.2</version> <configuration> <descriptors> <!-- 描述文件路径 --> <descriptor>src/main/assembly/bank-dist.xml</descriptor> </descriptors> </configuration> <executions> <execution> <!-- 绑定 package 阶段:每次打包自动组装 --> <id>make-distribution</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>

执行与产物验证:

mvn clean package # 输出关键行:Building zip: target/risk-engine-1.0.0-bank-dist.zip # 解压验证目录结构(运维验收口径) unzip -l target/risk-engine-1.0.0-bank-dist.zip # risk-engine-1.0.0/ # bin/start.sh bin/stop.sh 启动与停止脚本 # docs/CHANGELOG.md 版本说明 # lib/risk-engine-1.0.0.jar 程序本体 # lib/jackson-databind-2.13.4.jar 运行期依赖全套

启动脚本里用通配符拼 classpath:java -cp "lib/*" com.shop.risk.Main,依赖 jar 全部躺在 lib 目录,新增或升级依赖不需要改脚本。这就是目录式交付对运维环境的价值:依赖的增减对启动命令零感知。

现场翻车与修复

第一版交付当晚翻车:银行反馈启动报 NoSuchMethodError。回到依赖树一查,lib 目录里躺着两个不同大版本的同一构件——Assembly 默认忠实收录解析结果,而当时的 POM 恰好有多条路径引入同名构件。修复分两步:先按第 3 章的方法用 dependencyManagement 统一了版本;再在 dependencySet 加了一道保险:

<dependencySet> <outputDirectory>lib</outputDirectory> <scope>runtime</scope> <!-- 明确冲突时的取舍策略:优先取调解胜出版本 --> <useStrictFiltering>false</useStrictFiltering> </dependencySet>

更重要的教训是流程性的:组装类插件的产物必须做"启动冒烟"再出门。Assembly 只负责搬运文件,不校验 classpath 的完整性;内容对不对,只有真正加载一次才知道。作战室后来的规矩是交付包在离线沙箱里启动并调用健康检查,通过才允许移交。

💡 关键直觉:描述文件应随项目源码演进并纳入评审。交付物的内容(哪些文件、哪些依赖、什么权限)本质上是架构决策,散落在某个人的本地配置里,等于每次交付都在掷骰子。

战报小结

  • Assembly 的定位是组装器:fileSet 加 dependencySet 按蓝图拼装交付物,格式与目录结构完全自定义;
  • 目录式交付适合运维托管与离线环境,脚本用 lib 通配符拼 classpath,依赖增删零感知;
  • Assembly 不是冲突防线:它忠实搬运调解结果,版本统一要靠 dependencyManagement(第 3.3 节);
  • 产物必须启动冒烟:组装器不验证 classpath,离线沙箱跑一次健康检查是出门前的最后关口;
  • 与 Shade 分工:要目录要脚本用 Assembly,要单个合并 jar 用 Shade,Spring 服务直接用 repackage。

第 2 章收束。构建机器的传动结构、插件军团、多环境面孔与交付形态都已就位。下一章进入全册的核心战役——依赖战争:传递依赖如何蔓延、冲突如何定位、版本仲裁如何建制。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U