如何用Maven插件构建Spring Boot Fat Jar?构建失败问题排查
Hi there, let's tackle your two questions about building Spring Boot fat jars with Maven one by one:
一、如何正确使用Maven插件构建Spring Boot Fat Jar?
构建Spring Boot Fat Jar最常用的有两种方式,推荐优先使用官方插件:
1. 使用Spring Boot Maven Plugin(官方推荐)
这是Spring Boot官方提供的插件,专门用于打包可执行的Fat Jar,它会自动处理依赖打包、类路径配置以及主类入口等问题,配置非常简单:
在你的pom.xml的build/plugins中添加:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>your.main.class.FullyQualifiedName</mainClass> </configuration> </plugin>
执行命令mvn clean package后,会在target目录下生成两个Jar文件:一个是普通的Jar,另一个是带-exec后缀(或类似标识)的可执行Fat Jar,直接用java -jar target/your-app-exec.jar即可运行。
2. 使用maven-assembly-plugin
如果你需要自定义打包内容,可以使用这个插件,但需要注意Spring Boot的类加载机制,避免冲突。基本配置如下:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass>your.main.class.FullyQualifiedName</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
执行mvn clean package后,会生成一个带-jar-with-dependencies后缀的Fat Jar。
二、你的maven-assembly-plugin构建失败(无Jar生成)的问题分析与修复
先看一下你提供的pom.xml内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.github.vanroy</groupId> <artifactId>spring-data-jest-build</artifactId> <version>3.1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Spring Data Jest Build</name> <description>Build configuration of Spring Data Implementation for Jest</description> <url>https://github.com/vanroy/spring-data-jest</url> <developers> <developer> <id>vanroy</id> <name>Julien Roy</name> <email>julien.vanroy@gmail.com</email> <url>http://github.com/vanroy</url> <timezone>+1</timezone> <roles> <role>Java Developer</role> </roles> </developer> </developers> <licenses> <license> <name>Apache License, Version 2.0</name> <url>http://www.apache.org/licenses/LICENSE-2.0</url> </license> </licenses> <scm> <url>https://github.com/vanroy/spring-data-jest/</url> <connection>scm:git:ssh://git@github.com/vanroy/spring-data-jest.git</connection> <developerConnection>scm:git:ssh://git@github.com/vanroy/spring-data-jest.git</developerConnection> </scm> <properties> <!-- Java Version --> <java.version>1.8</java.version> <encoding>UTF-8</encoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <!-- Dependencies version --> <springboot>2.0.0.M4</springboot> <spring>5.0.0.RELEASE</spring> <springdataes>3.0.0.RELEASE</springdataes> <jest>5.3.2</jest> <gson>2.8.0</gson> <awssigning>0.0.16</awssigning> <springcloudaws>1.2.1.RELEASE</springcloudaws> <jna>4.2.2</jna> <hamcrest>1.3</hamcrest> <junit>4.12</junit> <lombok>1.16.8</lombok> <mockito>1.10.19</mockito> <logback>1.1.9</logback> <aws>1.11.132</aws> <elasticsearch>5.5.0</elasticsearch> <!-- Version of maven plugins --> <version.plugin.maven-compiler-plugin>3.6.1</version.plugin.maven-compiler-plugin> <version.plugin.maven-deploy-plugin>2.8.2</version.plugin.maven-deploy-plugin> <version.plugin.maven-surefire-plugin>2.20</version.plugin.maven-surefire-plugin> <version.plugin.maven-source-plugin>2.1.2</version.plugin.maven-source-plugin> <version.plugin.maven-javadoc-plugin>2.7</version.plugin.maven-javadoc-plugin> <version.plugin.maven-release-plugin>2.5</version.plugin.maven-release-plugin> <version.plugin.nexus-staging-maven-plugin>1.6.7</version.plugin.maven-nexus-staging-maven-plugin> <version.plugin.maven-gpg-plugin>1.6</version.plugin.maven-gpg-plugin> <version.plugin.maven-spring-boot-plugin>1.5.3.RELEASE</version.plugin.maven-spring-boot-plugin> <!-- Source encoding --> <source.encoding>UTF-8</source.encoding> </properties> <dependencyManagement> <dependencies> <!-- Spring Data Elasticsearch --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> <version>${springdataes}</version> <exclusions> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${elasticsearch}</version> </dependency> <!-- JEST --> <dependency> <groupId>io.searchbox</groupId> <artifactId>jest</artifactId> <version>${jest}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>${gson}</version> </dependency> <!-- Test --> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>${hamcrest}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>${hamcrest}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito}</version> <scope>test</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring}</version> <scope>test</scope> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jolira</groupId> <artifactId>onejar-maven-plugin</artifactId> <version>1.4.4</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>${version.plugin.maven-compiler-plugin}</version> <configuration> <compilerArgs> <arg>-Xlint</arg> </compilerArgs> </configuration> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>${version.plugin.maven-deploy-plugin}</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${version.plugin.maven-surefire-plugin}</version> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${version.plugin.maven-spring-boot-plugin}</version> </plugin> </plugins> </build> <repositories> <repository> <id>spring-libs-snapshot</id> <url>https://repo.spring.io/libs-snapshot</url> </repository> </repositories> <!-- Deploy snapshots to the Sonatype OSSRH (OSS Repository Hosting) --> <distributionManagement> <downloadUrl>https://github.com/vanroy/spring-data-jest</downloadUrl> <snapshotRepository> <id>ossrh</id> <url>https://oss.sonatype.org/content/repositories/snapshots</url> </snapshotRepository> <repository> <id>ossrh</id> <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> </repository> </distributionManagement> <profiles> <profile> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <modules> </modules> </profile> <profile> <id>release</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>${version.plugin.maven-source-plugin}</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <version>${version.plugin.maven-javadoc-plugin}</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>${version.plugin.maven-release-plugin}</version> <configuration> <autoVersionSubmodules>true</autoVersionSubmodules> <useReleaseProfile>false</useReleaseProfile> <releaseProfiles>release</releaseProfiles> <goals>deploy</goals> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.dfs.ace.catalog.api.SampleJestApplication.class</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles> </project>
问题根源分析
- 打包类型问题:你的
pom.xml中<packaging>pom</packaging>,这是Maven父项目的打包类型,不会生成Jar文件,只会处理依赖管理和模块聚合。如果这是一个Spring Boot应用项目,应该将<packaging>改为jar。 - 插件所在Profile未激活:
maven-assembly-plugin配置在releaseProfile中,而默认激活的是defaultProfile,执行mvn clean package时不会触发这个插件的执行。 - 主类配置错误:
<mainClass>中多了.class后缀,正确的应该是全限定类名,比如com.dfs.ace.catalog.api.SampleJestApplication,不需要加.class。 - Spring Boot插件与Assembly插件的冲突风险:你已经添加了
spring-boot-maven-plugin,但没有配置repackage目标,同时用Assembly插件打包可能会导致类加载问题,因为Spring Boot有自己的Fat Jar结构。
修复步骤
步骤1:修改打包类型
将<packaging>pom</packaging>改为:
<packaging>jar</packaging>
如果这确实是父项目,那么你需要在子项目的pom.xml中设置jar打包类型,并配置Assembly插件。
步骤2:激活Release Profile或移动插件配置
- 方式一:执行命令时激活Profile:
mvn clean package -Prelease - 方式二:将
maven-assembly-plugin的配置从releaseProfile移到根<build/plugins>下,这样默认执行就会触发。
步骤3:修正主类配置
将:
<mainClass>com.dfs.ace.catalog.api.SampleJestApplication.class</mainClass>
改为:
<mainClass>com.dfs.ace.catalog.api.SampleJestApplication</mainClass>
步骤4:优先使用Spring Boot Maven Plugin(推荐)
如果你不需要自定义打包内容,建议直接用Spring Boot官方插件,修改spring-boot-maven-plugin的配置:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${version.plugin.maven-spring-boot-plugin}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>com.dfs.ace.catalog.api.SampleJestApplication</mainClass> </configuration> </plugin>
执行mvn clean package后,就能在target目录得到可执行的Fat Jar。
内容的提问来源于stack exchange,提问作者Eugene Goldberg

