經過Spring Boot中的手動Bean定義提升啓動性能

原文:https://blog.csdn.net/qq_4288...react

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

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

徹底自動配置:Hello World WebFlux

做爲基準,讓咱們看一下具備單個HTTP端點的Spring Boot應用程序:編程

@SpringBootApplication
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

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

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

注意:若是你想嘗試這個代碼,很容易從Spring Initializr得到一個空的WebFlux應用程序。只需選中「Reactive Web」複選框並下載項目便可。spring-boot

手動導入自動配置

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

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

如今咱們只是使用咱們想要使用的那些功能,咱們能夠將其稱爲「點菜」方式,而不是「徹底自動配置autoconfiguration」中的「全部的你必須吃進去」。自動配置也其實自動尋找@Configuration標註的類,咱們可使用@Import替代@EnableAutoConfiguration,例如,如下是上面的應用程序,具備咱們想要的全部功能(不包括執行器):測試

@SpringBootConfiguration
@Import({
    WebFluxAutoConfiguration.class,
    ReactiveWebServerFactoryAutoConfiguration.class,
    ErrorWebFluxAutoConfiguration.class,
    HttpHandlerAutoConfiguration.class,
    ConfigurationPropertiesAutoConfiguration.class,
    PropertyPlaceholderAutoConfiguration.class
})
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

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

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

增長Actuators

若是咱們也能夠在類路徑上添加Actuator:

@SpringBootConfiguration
@Import({
    WebFluxAutoConfiguration.class,
    ReactiveWebServerFactoryAutoConfiguration.class,
    ErrorWebFluxAutoConfiguration.class,
    HttpHandlerAutoConfiguration.class,
    EndpointAutoConfiguration.class,
    HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
    InfoEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
    ReactiveManagementContextAutoConfiguration.class,
    ManagementContextAutoConfiguration.class,
    ConfigurationPropertiesAutoConfiguration.class,
    PropertyPlaceholderAutoConfiguration.class
})
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

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

Spring Boot可能會在未來作更多工做,以禁用未曝光或未使用過的執行器。

有什麼不一樣?

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

AutoConfigurationPackages
CodecsAutoConfiguration
JacksonAutoConfiguration
JmxAutoConfiguration
ProjectInfoAutoConfiguration
ReactorCoreAutoConfiguration
TaskExecutionAutoConfiguration
TaskSchedulingAutoConfiguration
ValidationAutoConfiguration
HttpMessageConvertersAutoConfiguration
RestTemplateAutoConfiguration
WebClientAutoConfiguration

這就是咱們不須要的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,咱們能夠寫一個自定義註釋:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface EnableWebFluxAutoConfiguration {
}

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

@SpringBootConfiguration
@EnableWebFluxAutoConfiguration
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

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

com.example.config.EnableWebFluxAutoConfiguration=\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

這樣作的好處是應用程序代碼再也不須要手動枚舉配置,並且如今由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失去其價值或受歡迎程度。

寫在最後:

既然看到這裏了,以爲筆者寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章
相關標籤/搜索