1. 引言
在本教程中,我們將探討通過 <i>mvn spring-boot:run</i> 命令啓動 Spring Boot Web 應用程序與通過 java -jar 命令運行編譯後的 jar/war 包之間的差異。
為了本教程的目的,我們假設您已經熟悉 Spring Boot 的 <em>repackage</em> 目標配置。有關此主題的更多詳細信息,請閲讀“使用 Spring Boot 創建胖 JAR 應用”。
2. Spring Boot Maven 插件
當編寫 Spring Boot 應用程序時,Spring Boot Maven 插件 是構建、測試和打包我們代碼的推薦工具。
該插件包含許多方便的功能,例如:
- 它能夠自動解決正確的依賴版本。
- 它可以將所有依賴項(包括嵌入式應用程序服務器,如果需要)打包到一個可執行的 fat jar/war 文件中,並且還會:
- 管理類路徑配置,因此我們無需在
java -jar命令中使用-cp選項。 - 實現自定義
ClassLoader來定位並加載現在嵌套在包內的所有外部 jar 庫。 - 自動查找
main()方法並將其配置在 manifest 中,因此我們無需在java -jar命令中指定主類。
- 管理類路徑配置,因此我們無需在
3. 使用 Maven 運行代碼(展開形式)
當我們開發Web應用程序時,可以利用 Spring Boot Maven 插件 的另一個非常有趣的功能:能夠自動將我們的Web應用程序部署到嵌入式應用服務器。
我們只需要一個依賴項,即可讓插件知道我們希望使用 Tomcat 運行代碼:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>現在執行項目根目錄下<em >mvn spring-boot:run</em > 命令時,插件會讀取 pom 配置文件,並理解我們需要一個 Web 應用程序容器。
執行 <em >mvn spring-boot:run</em > 命令會觸發 Apache Tomcat 的下載並初始化 Tomcat 的啓動:
$ mvn spring-boot:run
...
...
[INFO] --------------------< com.baeldung:spring-boot-ops >--------------------
[INFO] Building spring-boot-ops 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) > test-compile @ spring-boot-ops >>>
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom (1.8 kB at 2.8 kB/s)
...
...
[INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) @ spring-boot-ops ---
...
...
11:33:36.648 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
11:33:36.649 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.16]
...
...
11:33:36.952 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
...
...
11:33:48.223 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
11:33:48.289 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
11:33:48.292 [main] INFO org.baeldung.boot.Application - Started Application in 22.454 seconds (JVM running for 37.692)當日志中顯示包含“Started Application”的行時,我們的Web應用程序已準備好通過瀏覽器在地址http://localhost:8080/處進行查詢。
4. 以獨立打包應用程序運行代碼
在通過開發階段並準備將我們的應用程序部署到生產環境中時,我們需要打包我們的應用程序。
不幸的是,如果我們的工作基於 jar 包,基本的 Maven package 目標不包含任何外部依賴項。這意味着我們只能將其用作更大項目中的一個庫。
為了規避這一限制,我們需要利用 Maven Spring Boot 插件的 repackage 目標,以獨立應用程序的方式運行我們的 jar/war 文件。
4.1. 配置
通常,我們只需要配置構建插件:
<build>
<plugins>
...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
...
</plugins>
</build>由於我們的示例項目包含多個主類,因此我們需要告訴 Java 要運行哪個類,可以通過配置插件來實現:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<mainClass>com.baeldung.webjar.WebjarsdemoApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>或者設置 屬性:
<properties>
<start-class>com.baeldung.webjar.WebjarsdemoApplication</start-class>
</properties>4.2. 運行應用程序
現在,我們可以使用兩個簡單的命令運行我們的示例 WAR 文件:
$ mvn clean package spring-boot:repackage
$ java -jar target/spring-boot-ops.war關於如何運行 JAR 文件的更多詳細信息,請參閲我們的文章《使用命令行參數運行 JAR 應用程序》。
4.3. WAR 文件內部結構
為了更好地理解上述命令如何運行完整的服務器應用程序,我們可以深入研究我們的 `spring-boot-ops.war 文件。
如果將其解壓縮並查看內部內容,您會發現以下內容:
<em >META-INF</em >,包含自動生成的<em >MANIFEST.MF</em >文件。<em >WEB-INF/classes</em >,包含編譯後的類文件。- `WEB-INF/lib,包含 WAR 包的依賴項以及嵌入式 Tomcat jar 文件。
不過,還有一些特定於我們的胖包配置的文件夾:
- `WEB-INF/lib-provided,包含在嵌入式運行時所需的外部庫,但在部署時並非必需。
- `org/springframework/boot/loader,包含 Spring Boot 自定義類加載器。該庫負責加載外部依賴項並使其在運行時可訪問。
4.4. 戰爭部署清單
正如之前所述,Maven Spring Boot 插件會查找主類並生成用於運行 java 命令所需的配置。
生成的 MANIFEST.MF 文件包含一些額外的行:
Start-Class: com.baeldung.webjar.WebjarsdemoApplication
Main-Class: org.springframework.boot.loader.WarLauncher特別地,我們可以觀察到,它明確指定了使用 Spring Boot 類加載器啓動器。
4.5. 在 JAR 文件內部
由於默認打包策略,我們的 WAR 打包 場景與使用或不使用 Spring Boot Maven 插件 幾乎沒有區別。
為了更好地理解插件的優勢,我們可以嘗試更改 pom 文件的 打包 配置為 jar,然後再次運行 mvn clean package。
現在我們可以觀察到,我們的胖 JAR 文件與之前的 WAR 文件組織方式有所不同:
- 所有類和資源文件夾現在位於 BOOT-INF/classes 目錄下。
- BOOT-INF/lib 包含所有外部庫。
在沒有插件的情況下,lib 文件夾將不存在,並且 BOOT-INF/classes 目錄中的所有內容都位於包的根目錄下。
4.6. JAR 清單內部
MANIFEST.MF 也已更改,包含以下額外行:
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.3.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncherSpring-Boot-Classes 和 Spring-Boot-Lib 尤其有趣,因為它們告訴我們類加載器將如何查找類和外部庫。
5. 如何選擇
在分析工具時,務必考慮這些工具的創建目的。我們是希望簡化開發過程,還是確保平穩部署和可移植性?讓我們來審視哪些階段受此選擇影響最大。
5.1. 開發
作為開發者,我們通常花費大部分時間進行編碼,而無需花費大量時間設置本地運行代碼的環境。對於簡單的應用程序來説,這通常不是問題。但對於更復雜的項目,我們可能需要設置環境變量、啓動服務器以及填充數據庫。
每次想要運行應用程序時配置正確的環境會非常不切實際,尤其當多個服務同時運行時。
Maven 幫助我們運行代碼,因為我們已經將整個代碼庫本地檢查出來,因此我們可以利用 pom 配置文件和資源文件。我們可以設置環境變量、啓動內存數據庫,甚至下載正確的服務器版本並使用單個命令部署我們的應用程序。
即使在多模塊代碼庫中,其中每個模塊都需要不同的變量和服務器版本,我們也可以通過 Maven 配置文件輕鬆運行正確的環境。
5.2. 生產環境
隨着我們逐漸向生產環境過渡,對話的重點更多地轉向穩定性和安全性。因此,我們不能將用於開發機器的過程應用於具有實時客户的服務器。
在此時階段通過 Maven 運行代碼的做法是不可取的,原因如下:
- 首先,我們需要安裝 Maven。
- 然後,僅僅因為我們需要編譯代碼,我們就需要完整的 Java 開發工具包 (JDK)。
- 接下來,我們必須將代碼庫複製到我們的服務器,並將所有專有代碼以純文本形式保留。
mvn命令必須執行生命週期的所有階段(查找源、編譯和運行)。- 由於前一點,我們還會浪費 CPU,在雲服務器的情況下,還會浪費資金。
- Maven 啓動了多個 Java 進程,每個進程默認使用與父進程相同的內存量。
- 最後,如果我們需要部署到多個服務器,上述所有操作將在每個服務器上重複執行。
這些只是為什麼將應用程序作為軟件包交付到生產環境更實用的一些原因。
6. 結論
在本文中,我們探討了通過 Maven 和 java -jar 命令運行代碼之間的差異。我們還簡要回顧了一些實際案例場景。