ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Maven打包神器maven-assembly-plugin详解与实战

Maven打包神器maven-assembly-plugin详解与实战

1. Maven打包利器:maven-assembly-plugin深度解析

在Java项目构建领域,Maven作为主流构建工具已经存在了近20年。根据2023年最新的开发者调查报告显示,超过78%的Java项目仍在使用Maven进行依赖管理和项目构建。而在所有Maven插件中,maven-assembly-plugin以其强大的打包能力脱颖而出,成为处理复杂打包需求的标配工具。

我第一次接触这个插件是在2015年一个金融项目上,当时需要将项目打包成包含所有依赖的ZIP文件,同时还要保留特定的目录结构。在尝试了各种方案后,maven-assembly-plugin完美解决了所有需求。自此之后,它就成了我项目构建工具箱中的常驻成员。

2. 核心功能与工作原理

2.1 插件定位与核心价值

maven-assembly-plugin本质上是一个"打包组装器",它的核心功能可以概括为:

  • 将项目代码、依赖项、资源文件等元素按照自定义规则进行组合
  • 支持多种打包格式(ZIP、TAR、JAR等)
  • 允许通过描述符(descriptor)文件精细控制打包内容和结构

与maven-shade-plugin相比,assembly插件更擅长处理非JAR包的打包需求;而与maven-jar-plugin相比,它提供了更灵活的目录结构和文件包含/排除能力。

2.2 典型应用场景

在实际项目中,我经常在以下场景使用该插件:

  1. 制作包含所有依赖的"fat jar"(虽然shade插件更适合此场景)
  2. 构建分发包(包含bin、lib、conf等目录的标准发行包)
  3. 生成Docker镜像构建所需的目录结构
  4. 创建包含不同配置文件的定制化部署包
  5. 制作符合特定规范的软件安装包

3. 完整配置与使用指南

3.1 基础POM配置

首先需要在pom.xml中添加插件声明:

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <descriptors> <descriptor>src/assembly/distribution.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

3.2 描述符文件详解

描述符文件是assembly插件的核心配置,通常放在src/assembly目录下。下面是一个完整的distribution.xml示例:

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> <id>distribution</id> <formats> <format>zip</format> <format>tar.gz</format> </formats> <includeBaseDirectory>true</includeBaseDirectory> <baseDirectory>${project.artifactId}-${project.version}</baseDirectory> <fileSets> <!-- 包含运行时配置文件 --> <fileSet> <directory>src/main/resources</directory> <outputDirectory>conf</outputDirectory> <includes> <include>*.properties</include> <include>*.xml</include> </includes> </fileSet> <!-- 包含启动脚本 --> <fileSet> <directory>src/main/scripts</directory> <outputDirectory>bin</outputDirectory> <includes> <include>startup.sh</include> <include>shutdown.sh</include> </includes> <fileMode>0755</fileMode> </fileSet> </fileSets> <dependencySets> <!-- 主程序依赖 --> <dependencySet> <outputDirectory>lib</outputDirectory> <scope>runtime</scope> <useProjectArtifact>true</useProjectArtifact> </dependencySet> <!-- 可选:将依赖分类存放 --> <dependencySet> <outputDirectory>lib/ext</outputDirectory> <includes> <include>com.alibaba:*</include> <include>org.apache.*</include> </includes> <scope>runtime</scope> </dependencySet> </dependencySets> </assembly>

3.3 高级配置技巧

3.3.1 环境差异化打包

通过Maven profiles结合assembly插件,可以实现环境差异化打包:

<profiles> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>src/assembly/dev.xml</descriptor> </descriptors> </configuration> </plugin> </plugins> </build> </profile> <profile> <id>prod</id> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>src/assembly/prod.xml</descriptor> </descriptors> </configuration> </plugin> </plugins> </build> </profile> </profiles>
3.3.2 动态文件过滤

可以在打包时动态替换文件内容:

<fileSet> <directory>src/main/filters</directory> <outputDirectory>conf</outputDirectory> <filtered>true</filtered> <includes> <include>config.properties</include> </includes> </fileSet>

然后在config.properties中使用Maven属性:

db.url=${db.url} db.username=${db.username}

4. 实战案例:Spring Boot项目打包

4.1 与Spring Boot Maven插件协同工作

当项目使用spring-boot-maven-plugin时,需要特别注意执行顺序:

<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>exec</classifier> </configuration> </execution> </executions> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>src/assembly/boot.xml</descriptor> </descriptors> <appendAssemblyId>false</appendAssemblyId> </configuration> </execution> </executions> </plugin>

4.2 Spring Boot专属描述符示例

<assembly> <id>boot</id> <formats> <format>zip</format> </formats> <dependencySets> <dependencySet> <includes> <include>${project.groupId}:${project.artifactId}:jar:exec</include> </includes> <outputDirectory>/</outputDirectory> <unpack>false</unpack> </dependencySet> </dependencySets> <fileSets> <fileSet> <directory>${project.build.directory}</directory> <outputDirectory>/</outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> <fileSet> <directory>src/main/resources</directory> <outputDirectory>config</outputDirectory> </fileSet> </fileSets> </assembly>

5. 常见问题与解决方案

5.1 依赖冲突问题

当出现依赖冲突时,可以在dependencySet中使用excludes排除特定依赖:

<dependencySet> <outputDirectory>lib</outputDirectory> <excludes> <exclude>commons-logging:commons-logging</exclude> <exclude>log4j:log4j</exclude> </excludes> </dependencySet>

5.2 文件权限问题

在Linux环境下,需要特别注意文件权限:

<fileSet> <directory>src/main/scripts</directory> <outputDirectory>bin</outputDirectory> <fileMode>755</fileMode> <directoryMode>755</directoryMode> </fileSet>

5.3 路径引用问题

建议使用标准Maven属性来引用路径,避免硬编码:

<fileSet> <directory>${project.build.outputDirectory}</directory> <outputDirectory>classes</outputDirectory> </fileSet>

6. 性能优化建议

6.1 并行构建配置

在大型项目中,可以启用并行构建:

<configuration> <parallel>true</parallel> <threadCount>4</threadCount> </configuration>

6.2 增量构建支持

通过配置useProjectArtifact可以避免重复打包:

<dependencySet> <useProjectArtifact>false</useProjectArtifact> <useTransitiveDependencies>true</useTransitiveDependencies> </dependencySet>

6.3 缓存利用

合理配置attach可以控制是否安装/部署生成的包:

<configuration> <attach>true</attach> </configuration>

7. 最佳实践总结

经过多年使用,我总结了以下最佳实践:

  1. 保持描述符文件简洁,复杂的逻辑可以拆分成多个描述符
  2. 为不同环境创建不同的描述符文件
  3. 始终为输出目录设置合适的文件权限
  4. 在大型项目中使用并行构建提升性能
  5. 合理使用Maven属性使配置更灵活
  6. 为生成的包添加分类器(classifier)避免冲突
  7. 在CI/CD流水线中缓存依赖提升构建速度

一个典型的项目结构可能如下:

src/ assembly/ dev.xml prod.xml docker.xml main/ scripts/ startup.sh shutdown.sh resources/ application.properties pom.xml

通过合理使用maven-assembly-plugin,你可以为项目创建出完全符合生产要求的发布包,大大简化部署流程。这个插件的学习曲线虽然略陡峭,但一旦掌握,将成为你项目构建过程中的强大助力。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进