SpringApplication.run(xxx.class, args)背後的東東——整體脈絡

從spring到springmvc,再到springboot、springcloud,應用程式api開發調用方面都已經非常熟悉,但對spring背後的擴展機制:為何一個簡單的main方法可以實現這麼強大的功能,以及與第三方應用如何完美集成?帶著這些疑問,了解下springboot背後的整體脈絡。

 

大家都知道,對spring的擴展一般有三種方式:@import註解,實現ImportSeletor,以及實現ImportBeanDefinitionRegistrar介面,所以其實主要搞明白springboot底層是如何支援這三種方式就可以了。

 

首先,run方法執行時,會實例化spring容器,其實根據不同的環境實例化不同的容器類

context = createApplicationContext();

  servlet環境是實例化AnnotationConfigServletWebServerApplicationContext類,這裡最重要是註冊一些內置的BeanDefinitionRegistryPostProcessor(BeanFactoryPostProcessor的子介面),其中就包含重要ConfigurationClassPostProcessor,spring還特意為這種beandefinition取了beanname,如:org.springframework.context.annotation.internalConfigurationAnnotationProcessor。這裡有必要解釋下beandefinition,這個類個人認為是spring最重要的,spring容器管理的對象是我們創建的普通bean,但spring在創建之前,對這種bean用beandefinition進行了包裝和描述,進而可以在不同的擴展點去擴展bean的功能,底層是通過實現beanpostprocessor介面,同理咱們還可以實現beanfactorypostprocessor介面去擴展beandefinition,這也是spring設計的巧妙之處。

 

實例化工作完成之後會調用spring刷新容器的方法

org.springframework.context.support.AbstractApplicationContext#refresh

  接下來工作其實無非就是在哪個時機對剛剛spring進行註冊的beanfactorypostprocessor進行調用的問題,spring底層是委託PostProcessorRegistrationDelegate進行處理的。

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors

  最後就是回調ConfigurationClassPostProcessor等方法了

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

  @Import就是通過以下方法處理,將其配置資訊解析出來

org.springframework.context.annotation.ConfigurationClassParser#processImports

  最後,將其註冊到spring的BeanDefinitionRegistry中,整個擴展過程就完成了,剩下的就是spring如何根據beandefinition去實例化bean了。

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass

     

 回到最開始的問題,spring既然可以通過BeanDefinitionRegistryPostProcessor去動態的擴展beandefinition來實例化bean,那我們就可以利用這個擴展機制來集成第三方的工具到spring中,利用容器來管理這些bean,比如mybatis就是通過MapperScannerConfigurer來和spring整合的。