這些不知道,別說你熟悉 Spring

大家好,這篇文章跟大家來聊下 Spring 中提供的常用擴展點、Spring SPI 機制、以及 SpringBoot 自動裝配原理,重點介紹下 Spring 基于這些擴展點怎么跟配置中心(Apollo、Nacos、Zookeeper、Consul)等做集成 。
寫在前面我們大多數 Java 程序員的日常工作基本都是在做業務開發,俗稱 crudboy 。
作為 crudboy 的你有沒有這些煩惱呢?

  1. 隨著業務的迭代,新功能的加入,代碼變得越來越臃腫 , 可維護性越來越低,慢慢變成了屎山
  2. 【這些不知道,別說你熟悉 Spring】遇到一些框架層的問題不知道怎么解決
  3. 面試被問到使用的框架、中間件原理、源碼層東西,不知道怎么回答
  4. 寫了 5 年代碼了,感覺自己的技術沒有理想的長進
如果你有上述這些煩惱 , 我想看優秀框架的源碼會是一個很好的提升方式 。通過看源碼,我們能學到業界大佬們優秀的設計理念、編碼風格、設計模式的使用、高效數據結構算法的使用、魔鬼細節的巧妙應用等等 。這些東西都是助力我們成為一個優秀工程師不可或缺的 。
如果你打算要看源碼了,優先推薦 Spring、Netty、Mybatis、JUC 包 。
Spring 擴展我們知道 Spring 提供了很多的擴展點,第三方框架整合 Spring 其實大多也都是基于這些擴展點來做的 。所以熟練的掌握 Spring 擴展能讓我們在閱讀源碼的時候能快速的找到入口,然后斷點調試,一步步深入框架內核 。
這些擴展包括但不限于以下接口:
BeanFactoryPostProcessor:在 Bean 實例化之前對 BeanDefinition 進行修改
BeanPostProcessor:在 Bean 初始化前后對 Bean 進行一些修改包裝增強,比如返回代理對象
Aware:一個標記接口,實現該接口及子接口的類會收到 Spring 的通知回調,賦予某種 Spring 框架的能力,比如 ApplicationContextAware、EnvironmentAware 等
ApplicationContextInitializer:在上下文準備階段 , 容器刷新之前做一些初始化工作,比如我們常用的配置中心 client 基本都是繼承該初始化器,在容器刷新前將配置從遠程拉到本地 , 然后封裝成 PropertySource 放到 Environment 中供使用
ApplicationListener:Spring 事件機制,監聽特定的應用事件(ApplicationEvent) , 觀察者模式的一種實現
FactoryBean:用來自定義 Bean 的創建邏輯(Mybatis、Feign 等等)
ImportBeanDefinitionRegistrar:定義@EnableXXX 注解,在注解上 Import 了一個 ImportBeanDefinitionRegistrar,實現注冊 BeanDefinition 到容器中
InitializingBean:在 Bean 初始化時會調用執行一些初始化邏輯
ApplicationRunner/CommandLineRunner:容器啟動后回調 , 執行一些初始化工作
上述列出了幾個比較常用的接口,但是 Spring 擴展遠不于此,還有很多擴展接口大家可以自己去了解 。
Spring SPI 機制在講接下來內容之前,我們先說下 Spring 中的 SPI 機制 。Spring 中的 SPI 主要是利用 META-INF/spring.factories 文件來實現的 , 文件內容由多個 k = list(v) 的格式組成,比如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.dtp.starter.adapter.dubbo.autoconfigure.ApacheDubboTpAutoConfiguration,\com.dtp.starter.adapter.dubbo.autoconfigure.AlibabaDubboTpAutoConfigurationorg.springframework.boot.env.EnvironmentPostProcessor=\com.dtp.starter.zookeeper.autoconfigure.ZkConfigEnvironmentProcessor這些 spring.factories 文件可能是位于多個 jar 包中,Spring 容器啟動時會通過 ClassLoader.getResources() 獲取這些 spring.factories 文件的全路徑 。然后遍歷路徑以字節流的形式讀取所有的 k = list(v) 封裝到到一個 Map 中 , key 為接口全限定類名 , value 為所有實現類的全限定類名列表 。
上述說的這些加載操作都封裝在 SpringFactoriesLoader 類里 。該類很簡單,提供三個加載方法、一個實例化方法,還有一個 cache 屬性,首次加載到的數據會保存在 cache 里,供后續使用 。
這些不知道,別說你熟悉 Spring

文章插圖
SpringBoot 核心要點上面講的 SPI 其實就是我們 SpringBoot 自動裝配的核心 。
何為自動裝配?
自動裝配對應的就是手動裝配 , 在沒 SpringBoot 之前,我們使用 Spring 就是用的手動裝配模式 。在使用某項第三方功能時,我們需要引入該功能依賴的所有包,并測試保證這些引入包版本兼容 。然后在 XML 文件里進行大量標簽配置,非常繁瑣 。后來 Spring4 里引入了 JavaConfig 功能 , 利用 @Configuration + @Bean 來代替 XML 配置 , 雖然對開發來說是友好了許多 , 但是這些模板式配置代碼還是很繁瑣,會浪費大量時間做配置 。Java 重可能也就是這個時候給人留的一種印象 。

推薦閱讀