1、概览 {#1概览}
Apache Maven 是一种广泛使用的项目依赖管理工具和项目构建工具。
Spring Boot 通过 Spring Boot Maven Plugin 在 Apache Maven 中提供了对 Spring Boot 的支持。
众所周知,在 Maven 中可以使用 mvn package
将应用打包为 JAR 或 WAR 包。不过,Spring Boot Maven 插件额外添加了一个 repackage
goal,也可以在 mvn
命令中调用。
有时,这两个 mvn
命令会让人混淆。本文将带你了解 mvn package
和 spring-boot:repackage
之间的区别。
2、Spring Boot 应用示例 {#2spring-boot-应用示例}
首先,创建一个简单的 Spring Boot 应用作为示例:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
创建一个简单的 REST 端点来验证应用是否正常运行:
@RestController
public class DemoRestController {
@GetMapping(value = "/welcome")
public ResponseEntity welcomeEndpoint() {
return ResponseEntity.ok("Welcome to Baeldung Spring Boot Demo!");
}
}
3、Maven 的 package
Goal {#3maven-的-package-goal}
只需要 spring-boot-starter-web
依赖就能构建 Spring Boot 应用:
<artifactId>spring-boot-artifacts-2</artifactId>
<packaging>jar</packaging>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...
Maven 的 package
goal 会将编译后的代码打包成可发布的格式,在本例中就是 JAR
格式:
$ mvn package
[INFO] Scanning for projects...
[INFO] ------< com.baeldung.spring-boot-modules:spring-boot-artifacts-2 >------
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent ... /target/spring-boot-artifacts-2.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
执行 mvn package
命令后,可以在 target
目录下找到创建的 JAR 文件 spring-boot-artifacts-2.jar
。
来看看这个 JAR 文件的内容:
$ jar tf target/spring-boot-artifacts-2.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/baeldung/
com/baeldung/demo/
application.yml
com/baeldung/demo/DemoApplication.class
com/baeldung/demo/DemoRestController.class
META-INF/maven/...
从上面的输出中可以看到,mvn package
命令创建的 JAR 文件只包含项目源代码中的资源和已编译的 Java 类。
可以将此 JAR 文件作为另一个项目的依赖。但是,即使它是一个 Spring Boot 应用,也无法使用 java -jar JAR_FILE
执行该 JAR 文件。这是因为运行时依赖没有绑定。例如,没有启动 Web 上下文的 Servlet 容器。
要使用简单的 java -jar
命令启动 Spring Boot 应用,需要构建一个 Fat JAR。这就是 Spring Boot Maven 插件的用武之地。
4、Spring Boot Maven Plugin 的 repackage
Goal {#4spring-boot-maven-plugin-的-repackage-goal}
现在,来了解一下 spring-boot:repackage
的作用。
4.1、添加 Spring Boot Maven Plugin {#41添加-spring-boot-maven-plugin}
要执行 repackage
Goal,需要在 pom.xml
中添加 Spring Boot Maven 插件:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
4.2、执行 spring-boot:repackage
Goal {#42执行-spring-bootrepackage-goal}
现在,清理一下之前构建的 JAR 文件,然后试试 spring-boot:repackage
:
$ mvn clean spring-boot:repackage
...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
...
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli)
on project spring-boot-artifacts-2: Execution default-cli of goal
org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:repackage failed: Source file must not be null -> [Help 1]
...
有异常?这是因为 spring-boot:repackage
goal 将现有的 JAR 或 WAR 文件作为源文件,并将所有项目运行时依赖与项目类一起重新打包到最终构件中。这样,重新打包后的工件就可以使用命令行 java -jar JAR_FILE.jar
执行了。
因此,在执行 spring-boot:repackage
goal 之前,需要先构建 JAR 文件:
$ mvn clean package spring-boot:repackage
...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-boot-artifacts-2 ---
[INFO] Building jar: /home/kent/.../target/spring-boot-artifacts-2.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default-cli) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...
4.3、重新打包后的 JAR 文件内容 {#43重新打包后的-jar-文件内容}
现在,查看 target
目录,就会看到重新打包的 JAR 文件和原始 JAR 文件:
$ ls -1 target/*jar*
target/spring-boot-artifacts-2.jar
target/spring-boot-artifacts-2.jar.original
查看重新打包的 JAR 文件的内容:
$ jar tf target/spring-boot-artifacts-2.jar
META-INF/
META-INF/MANIFEST.MF
...
org/springframework/boot/loader/JarLauncher.class
...
BOOT-INF/classes/com/baeldung/demo/
BOOT-INF/classes/application.yml
BOOT-INF/classes/com/baeldung/demo/DemoApplication.class
BOOT-INF/classes/com/baeldung/demo/DemoRestController.class
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.xml
META-INF/maven/com.baeldung.spring-boot-modules/spring-boot-artifacts-2/pom.properties
BOOT-INF/lib/
BOOT-INF/lib/spring-boot-starter-web-2.3.3.RELEASE.jar
...
BOOT-INF/lib/spring-boot-starter-tomcat-2.3.3.RELEASE.jar
BOOT-INF/lib/tomcat-embed-core-9.0.37.jar
BOOT-INF/lib/jakarta.el-3.0.3.jar
BOOT-INF/lib/tomcat-embed-websocket-9.0.37.jar
BOOT-INF/lib/spring-web-5.2.8.RELEASE.jar
...
BOOT-INF/lib/httpclient-4.5.12.jar
...
很明显,上面的输出比 mvn package
命令构建的 JAR 文件要长得多。
在重新打包的 JAR 文件中,不仅有项目中已编译的 Java 类,还有启动 Spring Boot 应用程序所需的所有运行时库。例如,嵌入式 Tomcat 库被打包到 BOOT-INF/lib
目录中。
接下来,启动应用,检查它是否正常工作:
$ java -jar target/spring-boot-artifacts-2.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
2020-12-22 23:36:32.704 INFO 115154 [main] com.baeldung.demo.DemoApplication : Starting DemoApplication on YK-Arch with PID 11515...
...
2020-12-22 23:36:34.070 INFO 115154 [main] o.s.b.w.embedded.tomcat.TomcatWebServer: Tomcat started on port(s): 8080 (http) ...
2020-12-22 23:36:34.078 INFO 115154 [main] com.baeldung.demo.DemoApplication : Started DemoApplication in 1.766 seconds ...
Spring Boot 应用已启动,现在,调用 /welcome
端点来验证一下:
$ curl http://localhost:8080/welcome
Welcome to Baeldung Spring Boot Demo!
得到了预期的响应,程序正常运行。
4.4、在 Maven package 生命周期内执行 spring-boot:repackage Goal {#44在-maven-package-生命周期内执行-spring-bootrepackage-goal}
可以在 pom.xml
中配置 Spring Boot Maven 插件,以便在 Maven 生命周期的 package
阶段重新打包构件。换句话说,当执行 mvn package
时,spring-boot:repackage
将自动执行。
配置非常简单。只需将重 repackage
goal 添加到 execution
节点中即可:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
现在,再次运行 mvn clean package
:
$ mvn clean package
...
[INFO] Building spring-boot-artifacts-2 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:repackage (default) @ spring-boot-artifacts-2 ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...
输出结果显示 repackage
goal 已执行。检查文件系统,就会发现重新打包的 JAR 文件已经创建:
$ ls -lh target/*jar*
-rw-r--r-- 1 kent kent 29M Dec 22 23:56 target/spring-boot-artifacts-2.jar
-rw-r--r-- 1 kent kent 3.6K Dec 22 23:56 target/spring-boot-artifacts-2.jar.original
5、总结 {#5总结}
本文介绍了 mvn package
和 spring-boot:repackage
命令之间的区别,以及如何在 Maven 生命周期的 package
阶段执行 spring-boot:repackage
。
Ref:https://www.baeldung.com/spring-boot-repackage-vs-mvn-package