一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

使用Spring Boot時你不想使用@EnableAutoConfiguration。你應該怎麼做?Spring本質上是快速且輕量級的,但是如何讓Spring更快?其中一條建議是可以改善啟動時間,那就是考慮手動導入Spring Boot配置,而不是自動全部配置。

對所有應用程序來說,它不是正確的做法,但它可能會有所幫助,理解選項是什麼肯定不會有害。在本文中,我們將探討各種手動配置方法並評估其影響。

完全自動配置:Hello World WebFlux

作為基準,讓我們看一下具有單個HTTP端點的Spring Boot應用程序:

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

這個應用啟動大約一秒鐘,或者更長一些,具體取決於您的硬件。它在這段時間內做了很多工作 - 設置日誌系統,讀取和綁定配置文件,啟動Netty並偵聽端口8080,提供到@GetMapping應用程序的路由,還提供默認的錯誤處理。如果Spring Boot Actuator在類路徑上,你還會得到一個/ health和/ info端點(由於這個原因,啟動它需要更長的時間)。

@SpringBootApplication註釋實際包含@EnableAutoConfiguration功能,能夠自動提供所有有用的功能。這就是Spring Boot流行的原因,所以我們不想丟棄這個功能,但我們可以仔細看看實際發生的事情,也許可以手動完成一些,看看我們是否學到了什麼。

注意:如果你想嘗試這個代碼,很容易從Spring Initializr獲得一個空的WebFlux應用程序。只需選中“Reactive Web”複選框並下載項目即可。

手動導入自動配置

雖然@EnableAutoConfiguration可以輕鬆地為應用程序添加功能,但它也可以控制啟用哪些功能。大多數人都樂意做出妥協 ,但是過度追求易用性也會失控,可能存在性能損失 , 應用程序可能會啟動時慢一點,因為Spring Boot必須做一些工作才能找到所有這些功能並安裝它們。事實上,找到正確的功能並沒有太大的努力:首先類路徑掃描,經過仔細優化後,實現有條件的評估非常快。其中應用程序的批量啟動時間(80%左右)其實是由JVM加載類時間,因此實際上使其啟動更快的唯一方法是通過安裝更少的功能來讓JVM加載更少。

我們可以在註釋@EnableAutoConfiguration中使用exclude屬性禁用自動配置。一些單獨的自動配置也有自己的布爾配置標誌,可以在外部設置,例如我們可以使用的JMX spring.jmx.enabled=false(例如, 作為系統屬性或在屬性文件中)。我們可以走這條路並手動關閉我們不想使用的所有東西,但是這有點笨拙,並且如果類路徑改變也不會阻礙其他組件功能被發現。

現在我們只是使用我們想要使用的那些功能,我們可以將其稱為“點菜”方式,而不是“完全自動配置autoconfiguration”中的“所有的你必須吃進去”。自動配置也其實自動尋找@Configuration標註的類,我們可以使用@Import替代@EnableAutoConfiguration,例如,以下是上面的應用程序,具有我們想要的所有功能(不包括執行器):

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

此版本的應用程序仍將具有我們上面描述的所有功能,但啟動速度更快(可能大約30%左右)。那麼我們為了更快的啟動而放棄了什麼呢?這是一個快速的概述:

  • Spring Boot自動配置的完整功能包括實際應用程序中可能實際需要的其他內容,而不是特定的小樣本。換句話說,30%的加速並不適用於所有應用程序,您的情況可能會有所不同。
  • 手動配置很脆弱,很難猜到。如果您編寫了另一個執行稍微不同的應用程序,則需要進行不同的配置導入。您可以通過將其提取到便利類或註釋中並重新使用它來緩解此問題。
  • @Import行為方式與 @EnableAutoConfiguration配置類的排序方式不同。@Import在某些類具有依賴於早期類的條件行為的情況下,順序很重要,如果你的配置類有前後依賴順序關係,你必須要小心。
  • 在典型的實際應用中存在另一個排序問題。要模仿@EnableAutoConfiguration,首先需要處理用戶配置,以便它們可以覆蓋Spring Boot中的條件配置。如果使用@ComponentScan而不是@Imports,則無法控制掃描的順序,或者處理這些類的處理順序,您可以使用不同的註釋來緩解這種情況(參見下文)。
  • Spring Boot自動配置實際上從未被設計為以這種方式使用,使用這種方式可能會在您的應用程序中引入細微的錯誤。對此的唯一緩解方式就是是詳盡的測試,讓它以您期望的方式工作,並且對升級持謹慎態度。

增加Actuators

如果我們也可以在類路徑上添加Actuator:

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

與完整@EndpointAutoConfiguration應用程序相比,此應用程序啟動速度更快 (甚至可能快50%),因為我們只包含與兩個默認端點相關的配置。Spring Boot使用自動配置則默認地會激活所有端點,但不會將它們暴露給HTTP,如果我們只關心/ health和/ info,使用自動配置可能有些浪費,但自動配置也會在表中留下許多非常有用的功能。

Spring Boot可能會在將來做更多工作,以禁用未曝光或未使用過的執行器。例如,請參閱延遲執行器和 條件端點的問題(已經在Spring Boot 2.1.2中)。

有什麼不同?

手動配置的應用程序有51個bean,而完全引導的自動配置應用程序有107個bean(不計算執行器)。所以它啟動起來可能並不令人意外。在我們採用不同的方式實現示例應用程序之前,讓我們先看看我們遺漏了什麼,這樣才能更快地啟動它。如果在兩個應用程序中都列出bean定義,您將看到所有差異來自我們遺漏的自動配置,以及Spring Boot不會有條件地排除這些自動配置。這是列表(假設您使用spring-boot-start-webflux時沒有手動排除):

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

這就是我們不需要的12個自動配置(無論如何),並且在自動配置的應用程序中導致了56個額外的bean。它們都提供了有用的功能,所以我們可能希望有一天再次將它們包括在內,但是現在讓我們假設我們更願意在沒有他們的情況下使用。

spring-boot-autoconfigure有122個自動配置(還有更多spring-boot-actuator-autoconfigure)和完全引導的自動配置的示例應用程序上面只使用了其中的18個。計算使用哪些是非常早的,並且在任何類甚至加載之前,大多數都被Spring Boot丟棄,這種計算非常快(幾毫秒)

Spring Boot自動配置導入

可以通過使用不同的註釋來部分地解決與用戶配置(必須最後應用)和自動配置之間的差異相關聯的排序問題。Spring Boot為此提供了一個註釋:@ImportAutoConfiguration來自Spring Boot Test附帶spring-boot-autoconfigure的 測試切片功能。因此,您可以替換上面示例中的註釋@Import, @ImportAutoConfiguration效果是推遲自動配置的處理,直到所有用戶配置加載(例如,通過@ComponentScan或接收@Import)。

如果我們準備將自動配置列表整理成自定義註釋,我們甚至可以更進一步。不僅僅是直接使用@ImportAutoConfiguration,我們可以寫一個自定義註釋:

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

此註釋的主要特徵是它帶有元註釋 @ImportAutoConfiguration。有了這個,我們可以在我們的應用程序中添加新的註釋:

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

並列出實際的配置類/META-INF/spring.factories:

一文講解如何通過Spring Boot中的手動Bean定義提高啟動性能

這樣做的好處是應用程序代碼不再需要手動枚舉配置,而且現在由Spring Boot處理排序(屬性文件中的條目在使用之前會進行排序)。缺點是它僅對需要精確這些特徵的應用程序有用,並且需要在想要做一些不同的事情的任何應用程序中進行替換或擴充,當然它仍然會很快 - Spring Boot為排序做了一些額外的工作,但實際上並不是很多。在合適的硬件上,它可能仍然會在不到700毫秒的時間內啟動,並帶有正確的JVM標誌。

函數Bean定義

在前面的文章中,我提到函數bean定義將是使用Spring啟動應用程序的最有效方法。我們可以通過重新編寫所有Spring Boot自動配置為ApplicationContextInitializers來將這個應用程序額外擠出10%左右。

您可以手動執行此操作,或者您可以使用已為您準備的一些初始化程序,只要您不介意嘗試某些實驗性功能即可。目前有2個項目正在積極探索基於函數bean定義的新工具和新編程模型的概念:Spring Fu和 Spring Init。兩者都提供至少一組函數bean定義來替換或包裝Spring Boot自動配置。

Spring Fu是基於API(DSL)的,不使用反射或註釋;Spring Init具有函數bean定義,並且還具有用於“單點”配置的基於註釋的編程模型的原型。其他地方都有更詳細的介紹。

這裡要注意的要點是函數bean定義更快,但如果這是你主要考慮的問題,請記住它只有10%的效果。只要將所有功能放回到我們上面剝離的應用程序中,您就可以重新加載所有必需的類,並重新回到大致相同的啟動時間。換句話說,@Configuration本身運行時處理的成本 並不是完全可以忽略不計,但它也不是很高(在這些小應用程序中可能只有10%左右,或者可能是100毫秒)。

總結

Spring Boot自動配置非常方便,但可以稱之為“吃進所有你可以吃的東西”。目前(從2.1.x開始)它可能提供比某些應用程序使用或要求更多的功能。在“菜單單點”方法中,您可以使用Spring Boot作為準備和預測試配置的便捷集合,並選擇您使用的部件。如果你這樣做,那麼@ImportAutoConfiguration是工具包的一個重要部分,但是當我們進一步研究這個主題時,你應該如何最好地使用它。

Spring Boot的未來版本以及可能的其他新項目(如Spring Fu或Spring Init)將使得在運行時使用的配置選擇變得更加容易,無論是自動還是通過顯式選擇。注意,@Configuration本身在運行時處理並不是免費的,但它也不是特別昂貴(特別是使用Spring Boot 2.1.x)。您使用的功能數量越少,加載的類越少,啟動速度越快。最後,我們不希望 @EnableAutoConfiguration失去其價值或受歡迎程度。

寫在最後:

歡迎留言討論,如需Java方面的架構資料,我這裡剛好有一份,怎麼領取→→→關注+轉發 然後私信“架構資料” 即可領取

點關注,不迷路,持續更新!!!


分享到:


相關文章: