dubbo源碼解析-spi(3)
前言
在上一篇的末尾,我們提到了dubbo的spi中增加了IoC和AOP的功能.那麽本篇就講一下這個增加的IoC,spi部分預計會有四篇,因為這東西實在是太重要了.溫故而知新,我們先來回顧一下,我們之前都講了什麽.
- spi(1) 主要講了spi的基本概念,簡單的入門,並以spi為線索講解了雙親委托模式的弊端以及解決方案
- spi(2) 主要以dubbo改進了jdk的spi為線索,重點講分析問題的思路,從實際案例實戰從哪裏著手分析問題這個大家最喜歡問的問題.
提到IoC,大家第一個想到的就是Spring,所以Spring的IoC也是本篇的一大重點內容.當然畢竟主題是dubbo,所以對於Spring內容,將與dubbo結合,以短小精幹,但是又不缺乏深度的介紹.(後期如果大家有需要,也可以開展Spring源碼專題)
看到這裏可能有同學就會問,肥朝你不是寫dubbo源碼解析的嗎,為什麽還要講Spring呢?dubbo中涉及到很多的邊緣知識,其中包括Spring、Netty、Zookeeper等等,我希望的是,大家能通過學習dubbo為主線,全面綜合的提高自己,而不是為了看源碼而看源碼,也不是為了面試而看源碼.
插播面試題
你提到了dubbo中spi也增加了IoC,那你先講講Spring的IoC,然後再講講dubbo裏面又是怎麽做的
Spring的IoC
容器
Spirng的IoC容器主要有兩種,即BeanFactory和ApplicationContext
- BeanFactory是Spring中最底層的接口,只提供了最簡單的IoC功能,負責配置,創建和管理bean.
- ApplicationContext繼承了BeanFactory,擁有了基本的IoC功能外,還支持
- 國際化
- 消息機制
- 統一的資源加載
- AOP
另外ApplicationContext在加載的時候就會創建所有的bean(Web應用推薦),BeanFactory需要等到拿bean的時候才會創建bean(桌面應用推薦).所以,我們一般使用ApplicationContext
總體流程
實現IoC的過程,總體可以分為兩步,如下圖
容器啟動階段
該階段相當於"根據圖紙裝配成生產線",也就是對象管理信息的收集.
Bean實例化階段
拓展
Spring提供了BeanFactoryPostProcessor的容器拓展機制,該機制允許我們在容器實例化相應對象之前,對註冊到容器的BeanDefinition所保存的信息做相應的修改.
那我們有哪些實際場景有運用到這個拓展呢?
比如我們配置數據庫信息,經常用到占位符
${jdbc.url}
當BeanFactory在第一階段加載完成所有配置信息時,保存的對象的屬性信息還只是以占位符的形式存在.這個解析的工作是在PropertySourcesPlaceholderConfigurer中做的,我們來看看繼承體系圖就明白了.
PropertySourcesPlaceholderConfigurer實現了該接口,在進入第二階段時,已經把占位符信息替換完成.
溫馨提示:
細心的同學可能發現PropertySourcesPlaceholderConfigurer和PropertyPlaceholderConfigurer名字好像,這兩個有什麽區別?我們來看源碼
敲黑板劃重點
為何我反復強調基礎、原理,難道是為了騙你關註一下我,多一個粉絲?因為基礎紮實,明白原理之後很多東西真的是一通百通的,尤其了Spring Boot簡化了配置,很多問題就更考驗基礎了.我演示一個簡單的問題
首先我們發現這個占位符沒有被解析,如果不知道原理你可能一臉懵逼,但是看過肥朝博客的你,知道是缺少PropertySourcesPlaceholderConfigurer
於是你高高興興補上了PropertySourcesPlaceholderConfigurer,發現有坑,占位符是解析出來了,但是卻是null
然後你通過私信聯系到了肥朝,肥朝告訴你,加上個static
就可以了.於是你一運行,果然是棒棒噠!
但是你卻百思不得其解,為啥加上了一個static
就可以了呢?
原因很簡單,前面都說了,這個拓展機制是在實例化對象之前
,你用static
修飾方法,是屬於類級別的,優先級高,自然在DataSource實例化之前就完成了這個占位符的解析工作.
dubbo spi中的iOC
既然是源碼解析類文章,我就盡量避免貼大段代碼,否則還不如你直接去看.我用一個圖來粗略描述這個大致的過程
除了上一篇中對objectFactory的介紹外,從這裏我們知道,objectFactory就是dubbo的IoC提供對象.
public <T> T getExtension(Class<T> type, String name) {
//factories=[SpiExtensionFactory,SpringExtensionFactory]
//遍歷獲取spi,先從SpiExtensionFactory獲取,如果沒有,再從SpringExtensionFactory獲取
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
SpiExtensionFactory
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (loader.getSupportedExtensions().size() > 0) {
return loader.getAdaptiveExtension();
}
}
return null;
}
SpringExtensionFactory
public <T> T getExtension(Class<T> type, String name) {
for (ApplicationContext context : contexts) {
if (context.containsBean(name)) {
//從容器中獲取註入的對象
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
return null;
}
dubbo在設計的時候設計了這兩種方式,但是截止2.5.4版本,SpringExtensionFactory的方式尚未發現使用,可能像Java的保留字一樣,給以後埋下伏筆.據說3.0版本準備出來,到時候可以關註一下SpringExtensionFactory的使用情況.
Ref:
https://www.jianshu.com/p/718acbda838c
dubbo源碼解析-spi(3)