SpringBoot開發 - 什麼是熱部署和熱加載?devtool的原理是什麼?

在SpringBoot開發調試中,如果我每行代碼的修改都需要重啓啓動再調試,可能比較費時間;SpringBoot團隊針對此問題提供了spring-boot-devtools(簡稱devtools)插件,它試圖提升開發調試的效率。@pdai

準備知識點

什麼是熱部署和熱加載?

熱部署和熱加載是在應用正在運行的時候,自動更新(重新加載或者替換class等)應用的一種能力。(PS:spring-boot-devtools提供的方案也是要重啓的,只是無需手動重啓能實現自動加載而已。)

嚴格意義上,我們需要區分下熱部署和熱加載, 對於Java項目而言:

  • 熱部署

    • 在服務器運行時重新部署項目
    • 它是直接重新加載整個應用,這種方式會釋放內存,比熱加載更加乾淨徹底,但同時也更費時間。
  • 熱加載

    • 在在運行時重新加載class,從而升級應用。
    • 熱加載的實現原理主要依賴java的類加載機制,在實現方式可以概括爲在容器啓動的時候起一條後臺線程,定時的檢測類文件的時間戳變化,如果類的時間戳變掉了,則將類重新載入。
    • 對比反射機制,反射是在運行時獲取類信息,通過動態的調用來改變程序行爲; 熱加載則是在運行時通過重新加載改變類信息,直接改變程序行爲。

什麼是LiveLoad?

LiveLoad是提供瀏覽器客戶端自動加載更新的工具,分爲LiveLoad服務器和Liveload瀏覽器插件兩部分; devtools中已經集成了LiveLoad服務器,所以如果我們開發的是web應用,並且期望瀏覽器自動刷新, 這時候可以考慮LiveLoad.

同一時間只能運行一個LiveReload服務器。 開始應用程序之前,請確保沒有其他LiveReload服務器正在運行。如果從IDE啓動多個應用程序,則只有第一個應用程序將支持LiveReload。

配置devtools實現熱部署

我們通過如下配置來實現自動重啓方式的熱部署。

POM配置

添加spring-boot-devtools的依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional> <!-- 可以防止將devtools依賴傳遞到其他模塊中 -->
    </dependency>
</dependencies>

IDEA配置

如果你使用IDEA開發工具,通常有如下兩種方式:

  • 方式一: 無任何配置時,手動觸發重啓更新(Ctrl+F9)

(也可以用mvn compile編譯觸發重啓更新)

  • 方式二: IDEA需開啓運行時編譯,自動重啓更新

設置1

File->Setting->Build,Execution,Deployment->Compile

勾選:Make project automatically

設置2

快捷鍵:ctrl+alt+shift+/

選擇:Registry

勾選:compiler.automake.allow.when.app.running

新版本的IDEA可以在File->setting->Advanced Setttings裏面的第一個設置:

application.yml配置

spring:
  devtools:
    restart:
      enabled: true  #設置開啓熱部署
      additional-paths: src/main/java #重啓目錄
      exclude: WEB-INF/**
  thymeleaf:
    cache: false #使用Thymeleaf模板引擎,關閉緩存

使用LiveLoad

spring-boot-devtools模塊包含嵌入式LiveReload服務器,可以在資源更改時用於觸發瀏覽器刷新。 LiveReload瀏覽器擴展程序支持Chrome,Firefox和Safari,你可以從livereload.com免費下載。

或者從瀏覽器插件中心下載,比如firefox:

安裝完之後,可以通過如下圖標管理

如果你不想在應用程序運行時啓動LiveReload服務器,則可以將spring.devtools.livereload.enabled屬性設置爲false 。

同一時間只能運行一個LiveReload服務器。 開始應用程序之前,請確保沒有其他LiveReload服務器正在運行。如果從IDE啓動多個應用程序,則只有第一個應用程序將支持LiveReload。

進一步理解

雖然一些開發者會使用devtool工具,但是很少有能夠深入理解的;讓我們理解如下幾個問題,幫助你進一步理解。@pdai

devtool的原理?爲何會自動重啓?

爲什麼同樣是重啓應用,爲什麼不手動重啓,而是建議使用spring-boot-devtools進行熱部署重啓?

spring-boot-devtools使用了兩個類加載器ClassLoader,一個ClassLoader加載不會發生更改的類(第三方jar包),另一個ClassLoader(restart ClassLoader)加載會更改的類(自定義的類)。

後臺啓動一個文件監聽線程(File Watcher)監測的目錄中的文件發生變動時, 原來的restart ClassLoader被丟棄,將會重新加載新的restart ClassLoader

因爲文件變動後,第三方jar包不再重新加載,只加載自定義的類,加載的類比較少,所以重啓比較快。

這也是爲什麼,同樣是重啓應用,爲什麼不手動重啓,建議使用spring-boot-devtools進行熱部署重啓。

在自動重啓中有幾點需要注意:

  • 自動重啓會記錄日誌的

(記錄在什麼情況下重啓的日誌)

可以通過如下關閉

spring:
  devtools:
    restart:
      log-condition-evaluation-delta: false
  • 排除一些不需要自動重啓的資源

某些資源在更改時不一定需要觸發重新啓動。默認情況下,改變資源/META-INF/maven,/META-INF/resources,/resources,/static,/public,或/templates不觸發重新啓動,但確會觸發現場重裝。如果要自定義這些排除項,可以使用該spring.devtools.restart.exclude屬性。例如,要僅排除/static,/public你將設置以下屬性:

spring:
  devtools:
    restart:
      exclude: "static/**,public/**"

如果要保留這些默認值並添加其他排除項,請改用該spring.devtools.restart.additional-exclude屬性。

  • 自定義重啓類加載器

重啓功能是通過使用兩個類加載器來實現的。對於大多數應用程序,這種方法效果很好。但是,它有時會導致類加載問題。

默認情況下,IDE 中的任何打開項目都使用“重啓”類加載器加載,任何常規.jar文件都使用“基本”類加載器加載。如果你處理一個多模塊項目,並且不是每個模塊都導入到你的 IDE 中,你可能需要自定義一些東西。爲此,你可以創建一個META-INF/spring-devtools.properties文件。

該spring-devtools.properties文件可以包含以restart.exclude和爲前綴的屬性restart.include。該include元素是應該被拉高到“重啓”的類加載器的項目,以及exclude要素是應該向下推入“Base”類加載器的項目。該屬性的值是應用於類路徑的正則表達式模式,如以下示例所示:

restart:
  exclude:
    companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar"
  include:
    projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"

更多相關的信息可以在這裏查看。

devtool是否會被打包進Jar?

devtool原則上來說應該是隻在開發調試的時候使用,而在生產環境運行jar包時是不需要的,所以Spring打包會不會把它打進JAR嗎?

  • 默認情況下,不會被打包進JAR

運行打包的應用程序時,開發人員工具會自動禁用。如果你通過 java -jar或者其他特殊的類加載器進行啓動時,都會被認爲是“生產環境的應用”。

  • 如果我們期望遠程調試應用

生產環境勿用,只有在受信任的網絡上運行或使用 SSL 進行保護時,才應啓用它

在這種情況下,devtool也具備遠程調試的能力:遠程客戶端應用程序旨在從你的 IDE 中運行。你需要org.springframework.boot.devtools.RemoteSpringApplication使用與你連接的遠程項目相同的類路徑運行。應用程序的唯一必需參數是它連接到的遠程 URL。

例如,如果使用 Eclipse 或 Spring Tools,並且你有一個my-app已部署到 Cloud Foundry 的名爲的項目,執行以下操作:

  1. 選擇Run Configurations…​從Run菜單。
  2. 創建一個新的Java Application“啓動配置”。
  3. 瀏覽my-app項目。
  4. 使用org.springframework.boot.devtools.RemoteSpringApplication作爲主類。
  5. 添加https://myapp.cfapps.io到Program arguments(或任何你的遠程 URL)。

正在運行的遠程客戶端可能類似於以下列表:

  .   ____          _                                              __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _          ___               _      \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | _ \___ _ __  ___| |_ ___ \ \ \ \
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote :: 2.5.4

2015-06-10 18:25:06.632  INFO 14938 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-project/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code)
2015-06-10 18:25:06.671  INFO 14938 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy
2015-06-10 18:25:07.043  WARN 14938 --- [           main] o.s.b.d.r.c.RemoteClientConfiguration    : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2015-06-10 18:25:07.074  INFO 14938 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2015-06-10 18:25:07.130  INFO 14938 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)

devtool爲何會默認禁用緩存選項?

Spring Boot 支持的一些庫使用緩存來提高性能。例如,模板引擎緩存已編譯的模板以避免重複解析模板文件。此外,Spring MVC 可以在提供靜態資源時向響應添加 HTTP 緩存標頭。

雖然緩存在生產中非常有益,但在開發過程中可能會適得其反,使你無法看到剛剛在應用程序中所做的更改。出於這個原因, spring-boot-devtools 默認禁用緩存選項。

比如Thymeleaf 提供了spring.thymeleaf.cache來設置模板引擎的緩存,使用spring-boot-devtools模塊時是不需要手動設置這些屬性的,因爲spring-boot-devtools會自動進行設置。

那麼會自動設置哪些配置呢?你可以在DevToolsPropertyDefaultsPostProcessor類找到對應的默認配置。

public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {

	static {
		Map<String, Object> properties = new HashMap<>();
		properties.put("spring.thymeleaf.cache", "false");
		properties.put("spring.freemarker.cache", "false");
		properties.put("spring.groovy.template.cache", "false");
		properties.put("spring.mustache.cache", "false");
		properties.put("server.servlet.session.persistent", "true");
		properties.put("spring.h2.console.enabled", "true");
		properties.put("spring.web.resources.cache.period", "0");
		properties.put("spring.web.resources.chain.cache", "false");
		properties.put("spring.template.provider.cache", "false");
		properties.put("spring.mvc.log-resolved-exception", "true");
		properties.put("server.error.include-binding-errors", "ALWAYS");
		properties.put("server.error.include-message", "ALWAYS");
		properties.put("server.error.include-stacktrace", "ALWAYS");
		properties.put("server.servlet.jsp.init-parameters.development", "true");
		properties.put("spring.reactor.debug", "true");
		PROPERTIES = Collections.unmodifiableMap(properties);
	}

當然如果你不想被應用屬性被spring-boot-devtools默認設置, 可以通過spring.devtools.add-properties到false你application.yml中。

devtool是否可以給所有Springboot應用做全局的配置?

可以通過將spring-boot-devtools.yml文件添加到$HOME/.config/spring-boot目錄來配置全局 devtools 設置

添加到這些文件的任何屬性都適用於你機器上使用 devtools 的所有Spring Boot 應用程序。例如,要將重新啓動配置爲始終使用觸發器文件,你需要將以下屬性添加到你的spring-boot-devtools文件中:

spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

如果我不用devtool,還有什麼選擇?

如果我不用devtool,還有什麼選擇?

在實際的開發過程中,我也不會去使用devtool工具, 因爲:

  • devtool本身基於重啓方式,這種仍然不是真正的熱替換方案,JRebel纔是(它是收費的)
  • 開發調試最重要的還是一種權衡
    • 自動重啓的開銷如果和手動重啓沒有什麼太大差別,那麼還不如手動重啓(按需重啓)
    • 多數情況下,如果是方法內部的修改或者靜態資源的修改,在IDEA中是可以通過Rebuild(Ctrl + Shift + F9)進行熱更的

  • 此外還有一個工具spring loaded, 可實現修改類文件的熱部署,具體可看其github地址上的說明。

示例源碼

https://github.com/realpdai/tech-pdai-spring-demos

參考文章

https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.devtools

https://liayun.blog.csdn.net/article/details/116541775

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章