SpringBoot開發

2022-07-10 18:02:09

在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

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