SpringBoot魔法堂:應用熱部署實踐與原理淺析

^_^肥仔John發表於2020-12-15
SpringBoot魔法堂:應用熱部署實踐與原理淺析

前言

後端開發的同學想必每天都在重複經歷著修改程式碼、執行程式碼編譯,等待……重啟Tomcat服務,等待……最後測試發現還是有bug,然後上述流程再來一遍(我聽不見)?
能不能像前端開發的同學那樣,修改程式碼儲存檔案後自動編譯、重新載入應用呢?Spring Boot給了我們一個大大的Yes!
本文我們就一起來探索Spring Boot的熱部署功能提升開發效率吧!

長話短說

熱部署作為開發階段的特性,由spring-boot-devtools模組提供,用於在修改類、配置檔案和頁面等靜態資源後,自動編譯Spring Boot應用和載入應用和頁面靜態資源,從而提高開發流程自動化程度提升開發效率。
那麼第一步當然是在pom.xml中新增配置:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-devtools</artifactId>
  <option>true</option>
</dependency>

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <fork>true</fork> <!-- 預設值為false,必須設定為true才能啟用熱部署功能(具體原因請見下文) -->
      </configuration>
    </plugin>
  </plugins>
</build>

靜態資源熱部署

對於HTML頁面、圖片、CSS樣式檔案這些顯然不需要編譯的靜態資源,Spring Boot Devtools模組通過內建的livereload服務端和瀏覽器的LiveReload外掛共同實現熱部署。

  1. 服務端配置
spring:
  devtools:
    livereload:
      enabled: true # 啟用LiveReload服務端
      port: 35729 # LiveReload服務埠

預設僅觸發LiveReload事件的預設路徑如下: /META-INF/maven,/META-INF/resources,/resources,/static,/public/templates

  1. 瀏覽器配置
    無論時FireFox還是Chrome都有相應的LiveReload外掛,按步驟安裝就可以了。

Java類資源熱部署

Spring Boot Devtools模組是通過監聽Java類資源變化觸發應用熱部署,請注意這裡監聽的是Java類資源而不是Java原始碼檔案,那麼什麼是Java類資源**呢?其實就是.class檔案。
這樣從儲存Java原始碼檔案到Spring Boot Devtools監聽到Java類資源變化之間,就有一道不可逾越的鴻溝了。我們必須通過額外手段填平:

  1. 手動方式:修改Java原始碼檔案後,執行mvn compile
  2. 自動方式:配置IDEA監聽Java原始碼檔案變化,觸發重新編譯
    2.1. 右鍵點選SpringBootApplication入口類檔案,並點選Create XXXX.main(),建立Application型別的Configuration;
    2.2. 勾選File/Settings/Compiler/Build Project automatically
    2.3. 按ctrl+shift+alt+/,然後選擇Registry並勾選Compiler autoMake allow when app running
    2.4. 通過IDEA左上角綠色的執行按鈕啟動Spring Boot應用,然後修改Java原始碼檔案後IDEA會自動重新編譯專案,從而觸發Spring Boot Devtools熱部署。

更多配置配置項

spring:
  devtools:
  restart:
    enabled: true # 啟用熱部署
    exclude: main/static/** # 除預設路徑外,新增檔案變化不觸發熱部署的路徑列表,多個路徑之間通過逗號分隔。
    additional: assets/** # 新增檔案變化會觸發熱部署的路徑列表,多個路徑之間通過逗號分隔。
    additional-exclude: assets/public/** # 設定additional屬性指定的路徑下某些路徑的檔案變化,不觸發熱部署,多個路徑之間通過逗號分隔。

預設不觸發熱部署的路徑有:/META-INF/maven,/META-INF/resources,/resources,/static,/public/templates

除了通過yml檔案配置是否啟用熱部署功能外,還可以通過環境變數設定。在SpringBootApplication入口方法中加入

System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApp.class, args);

疑難解答

  1. 在IDEA中修改檔案後報 Maven Resource Compiler: Maven project configuration required for module 'lkm-api' isn't available. Compilation of Maven projects is supported only if build is started from an IDE.
    答:請使用IDEA那個綠色的執行按鈕啟動Spring Boot應用。
  2. 在IDEA中修改檔案後沒有反應
    答:請稍等數秒自然會觸發重新編譯和熱部署的。

為什麼是熱部署而不是熱替換呢?

開發過React或Vue的同學對熱替換應該不陌生吧,可以粗線條地理解為將應用以比檔案更細粒度的模組或函式來組織,當原始碼發生變化時僅僅替換髮生變化的模組或函式以及依賴它們的模組或函式,通過最小化變更達到快速更新應用狀態。
而Spring Boot Devtools並沒有做成像React和Vue的開發工具那麼細粒度的更新,而是採取通過基類載入器重啟類載入器兩個類載入器來實現熱部署:

  1. 基類載入器,用於載入第三方依賴等開發階段不經常發生變化的Java類資源。
  2. 重啟類載入器,用於載入當前專案的Java類資源。若當前專案的Java類資源發生變化時,正在執行的重啟類載入器會被丟棄,並另外建立一個重啟類載入器並載入最新的Java類資源。

為什麼pom.xml檔案中的spring-boot-maven-plugin要設定為獨立JVM程式執行呢(<fork>true</fork>)?

預設情況下<fork>false</fork>表示Maven採用執行自身的JVM虛擬機器執行外掛,而通過<fork>true</fork>則告知Maven啟動一個新的JVM虛擬機器程式執行外掛。
那麼為什麼要耗費資源啟動新JVM虛擬機器執行外掛呢?直接執行不香嗎?

場景1——使用不同的JDK執行外掛

執行mvn -v會顯示當前Maven執行的JDK版本資訊,假設為JDK1.8且編碼方式為UTF-8。
由於Maven 3.8.1必須執行在JDK1.8以上,而專案只能在JDK1.6上編譯執行,因此需要通過如下方式執行外掛:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.1</version>
  <configuration>
    <fork>true</fork>
    <!-- 指定JDK家目錄,預設為環境變數PATH中的路徑 -->
    <executable>/path/to/jdk1.6</executable>
    <compilerVersion>1.3</compilerVersion>
  </configuration>
</plugin>

場景2——採用不同的JVM配置執行外掛

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.1</version>
  <configuration>
      <fork>true</fork>
      <meminitial>128m</meminitial>
      <maxmem>1024m</maxmem>
      <compilerArgs>
        <arg>-XX:MaxPermSize=256m</arg>
      </compilerArgs>
  </configuration>
</plugin>

場景3——外掛需要特定的JVM配置來執行

像spring-boot-maven-plugin那樣在啟用spring-boot-devtools模組時需要特定JVM配置來執行,並且執行途中還會對重啟類載入器慘下殺手的,自然也要建立新的JVM虛擬機器程式來執行才可以了。

總結

Spring Boot不單單通過約定由於配置的原則簡化了過去Spring MVC那些繁瑣的配置檔案,還提供各種顯著提升開發效率的自動化工具,而spring-boot-devtools就是其中一個。
倘若你所在的團隊還沒用上Spring Boot那麼是不是就無法享受這份便捷呢?我想JRebel IDEA外掛應該是你需要的:)

轉載請註明來自:https://www.cnblogs.com/fsjohnhuang/p/14136491.html —— 肥仔John

相關文章