使用Maven Assembly聚合子模块时如何跳过子模块构建
解决方案与问题解答
使用的配置
- Maven:3.6.3
- Maven Assembly:3.3.0
核心矛盾解决:既聚合子模块内容又避免重复构建
你遇到的问题本质是:父pom作为聚合模块,要么显式声明子模块触发构建,要么无法访问子模块的构建产物/源码。这里提供两种可行方案:
方案1:通过moduleSets配置+全局跳过子模块构建(仅在目标Profile生效)
在父pom的Profile里,配置Assembly插件的moduleSets包含所有子模块,同时添加全局属性跳过子模块的编译、测试等构建步骤,无需修改任何子模块pom:
<profile> <id>assemble-all</id> <properties> <!-- 全局跳过子模块的编译、测试、打包流程 --> <maven.compiler.skip>true</maven.compiler.skip> <maven.test.skip>true</maven.test.skip> <maven.jar.skip>true</maven.jar.skip> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>assemble-all</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>${project.basedir}/src/main/assembly/module-assembly.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
对应的通用描述符module-assembly.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>all-modules</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <moduleSets> <moduleSet> <!-- 自动包含父模块下的所有子模块 --> <includeSubModules>true</includeSubModules> <sources> <includeModuleDirectory>true</includeModuleDirectory> <!-- 打包子模块的源码和资源 --> <fileSets> <fileSet> <directory>${module.basedir}/src/main/java</directory> <outputDirectory>/</outputDirectory> </fileSet> <fileSet> <directory>${module.basedir}/src/main/resources</directory> <outputDirectory>/</outputDirectory> </fileSet> </fileSets> </sources> </moduleSet> </moduleSets> </assembly>
注意:第一次执行或修改子模块代码后,需先执行mvn clean install构建子模块,之后再激活Profile执行聚合:mvn clean package -P assemble-all,这样既不会重复构建子模块,又能获取最新的源码/资源。
方案2:通过dependencySet解压子模块Jar打包(适合已构建好的子模块)
如果不想直接操作源码,而是用子模块的构建产物Jar,可在父pom中依赖所有子模块,再通过Assembly的dependencySet解压合并:
父pom的Profile配置:
<profile> <id>assemble-all-jars</id> <dependencies> <!-- 添加所有子模块依赖 --> <dependency> <groupId>${project.groupId}</groupId> <artifactId>service-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>service-impl</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>core</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>facade</artifactId> <version>${project.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>assemble-all-jars</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>${project.basedir}/src/main/assembly/jar-assembly.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
通用描述符jar-assembly.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>all-jars</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <!-- 仅包含当前父模块的子模块依赖 --> <includes> <include>${project.groupId}:service-api</include> <include>${project.groupId}:service-impl</include> <include>${project.groupId}:core</include> <include>${project.groupId}:facade</include> </includes> <!-- 解压所有子模块Jar到根目录 --> <unpack>true</unpack> <!-- 排除传递依赖,解决你之前Shade插件的问题 --> <useTransitiveDependencies>false</useTransitiveDependencies> </dependencySet> </dependencySets> </assembly>
这种方式需要子模块已经构建并安装到本地仓库,执行命令同样是mvn clean package -P assemble-all-jars,无需额外参数。
针对你的疑问逐一解答
- 遗漏的配置/属性:方案1中使用的
maven.compiler.skip等全局跳过属性,以及Assembly的includeSubModules配置,是你之前没用到的关键;方案2的useTransitiveDependencies可直接解决传递依赖过滤问题。 - 通用命令:上述两种方案都只需激活对应Profile,无需
-pl或其他模块指定参数,命令统一简洁。 - 单次设置跳过子模块构建:方案1通过在父pom的Profile中定义全局跳过属性,无需修改任何子模块pom,完全满足需求。
- 使用fileSets的可行性:完全可以。如果子模块结构完全一致,通用描述符里可以用
fileSets直接指定子模块的源码/资源目录,比如:
缺点是需要在描述符里枚举所有子模块路径,但胜在无需依赖子模块构建状态,直接读取源码。<fileSets> <fileSet> <directory>${project.basedir}/service-api/src/main/java</directory> <outputDirectory>/</outputDirectory> </fileSet> <fileSet> <directory>${project.basedir}/service-impl/src/main/resources</directory> <outputDirectory>/</outputDirectory> </fileSet> <!-- 其他子模块路径依次添加 --> </fileSets> - 解压子模块Jar重新打包:方案2就是这种实现,唯一需要注意的是修改代码后必须先执行
mvn clean install构建子模块,确保父pom能获取到最新的Jar包。
额外建议
如果Shade插件的传递依赖过滤问题是核心痛点,可以尝试在Shade插件中配置filters精准排除不需要的依赖,比如:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> <!-- 排除特定传递依赖 --> <exclude>com.example:unwanted-dependency</exclude> </excludes> </filter> </filters> </configuration> </execution> </executions> </plugin>
Shade插件本身就是为合并Jar设计的,比Assembly更适合处理依赖冲突和过滤,你可以再尝试调整。
内容的提问来源于stack exchange,提问作者Florent JITIAUX
相关产品推荐
相关产品推荐

