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插件共同實現熱部署。
- 服務端配置
spring:
devtools:
livereload:
enabled: true # 啟用LiveReload服務端
port: 35729 # LiveReload服務埠
默認僅觸發LiveReload事件的默認路徑如下: /META-INF/maven,/META-INF/resources,/resources,/static,/public和/templates。
- 瀏覽器配置
無論時FireFox還是Chrome都有相應的LiveReload插件,按步驟安裝就可以了。
Java類資源熱部署
Spring Boot Devtools模組是通過監聽Java類資源變化觸發應用熱部署,請注意這裡監聽的是Java類資源而不是Java源程式碼文件,那麼什麼是Java類資源**呢?其實就是.class文件。
這樣從保存Java源程式碼文件到Spring Boot Devtools監聽到Java類資源變化之間,就有一道不可逾越的鴻溝了。我們必須通過額外手段填平:
- 手動方式:修改Java源程式碼文件後,執行
mvn compile - 自動方式:配置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);
疑難解答
- 在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應用。 - 在IDEA中修改文件後沒有反應
答:請稍等數秒自然會觸發重新編譯和熱部署的。
為什麼是熱部署而不是熱替換呢?
開發過React或Vue的同學對熱替換應該不陌生吧,可以粗線條地理解為將應用以比文件更細粒度的模組或函數來組織,當源程式碼發生變化時僅僅替換髮生變化的模組或函數以及依賴它們的模組或函數,通過最小化變更達到快速更新應用狀態。
而Spring Boot Devtools並沒有做成像React和Vue的開發工具那麼細粒度的更新,而是採取通過基類載入器和重啟類載入器兩個類載入器來實現熱部署:
- 基類載入器,用於載入第三方依賴等開發階段不經常發生變化的Java類資源。
- 重啟類載入器,用於載入當前項目的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插件應該是你需要的:)
轉載請註明來自://www.cnblogs.com/fsjohnhuang/p/14136491.html —— 肥仔John

