ExtensionLoader是Dubbo中很有特色的一個設計,它的作用是爲框架提供各種組件的擴展點,可以在應用運行時來決定使用哪個組件。對擴展點組件的描述是通過註解的方式實現的,包括3個主要的註解:
- SPI
- Adaptive
- Activate
我們先從註解的含義來理解ExtensionLoader
SPI
SPI是一個很早就存在的概念,它是預先定義的一組接口,並允許各個廠商(使用方)有自己的實現,通過配置的方式在運行時決定使用哪種實現。我們可以看一下Dubbo中具有SPI標記的接口有哪些
- CacheFactory
- Compiler
- ExtensionFactory
- LoggerAdapter
- Serialization
- StatusChecker
- DataStore
- ThreadPool
- Container
- PageHandler
- MonitorFactory
- RegistryFactory
- ChannelHandler
- Codec
- Codec2
- Dispatcher
- Transporter
- Exchanger
- HttpBinder
- Networker
- TelnetHandler
- ZookeeperTransporter
- ExporterListener
- Filter
- InvokerListener
- Protocol
- ProxyFactory
- Cluster
- ConfiguratorFactory
- LoadBalance
- Merger
- RouterFactory
- RuleConverter
- ClassNameGenerator
- Validation
當我們需這些接口的實現類時,就可以通過ExtensionLoader的方法獲取
其中第一個方法是拿到一個具體的實現類,第二個方法是拿到一組被激活的實現類。
Adaptive
這個詞無論作爲註解名還是get方法名都比較令人迷惑。Adaptive的本意是適配的,之所以這樣命名是因爲我們從getAdaptiveExtension()獲取到的對象並不是某個真正的實現類,而是對實現類進行封裝之後的一個適配器。當我們調用這個適配器的方法時,它會間接地去調用具體實現類的方法。而適配器中正包含了該選取何種實現方式的邏輯。
適配器類的定義有兩種來源:
- 靜態代碼形式的默認適配器。這些類會被Adaptive註解修飾,且一個接口只能有一個這樣的靜態適配器。這種形式僅應用於一些特殊的接口,如:AdaptiveCompiler、AdaptiveExtensionFactory這兩個適配器,ExtensionLoader需要依賴它們來工作,所以使用了這種特殊的構建方式。
- 動態代碼適配器。實際上其餘的接口都是使用動態適配器,ExtensionLoader根據接口定義動態生成一段適配器代碼,並構建這個動態類的實例。這個時候接口中的一些方法具有Adaptive標記,它提供了一些用於查找具體Extension的key,如果這些方法中有URL類型的參數,則會依次在url中查找這些key對應的value,再以此爲name確定要使用的Extension。如果沒有從url中找到該參數,則會使用SPI註解中的默認值name進行構建。
Transporter
下面以Transporter接口爲例進行說明
bind方法具有Adaptive標記,所以在生成動態Adapter類的時候,該方法會根據運行時的參數決定使用那個具體的實現類,以下是部分動態類的代碼
在適配器的方法中,首先要確定具體實現類的類型,也就是extName。當方法中有URL類型的參數時,這個配置會從url參數中獲取。Adaptive註解中的兩個key即是在url查找的key,分別爲server和transporter。當兩個key都不存在時,使用SPI註解中得默認值netty。在確定了extName後,就可以從事先加載好的實例中獲取extension對象,最終將方法的調用傳遞給extension的對應方法。
全部extName與實現類的對應關係配置在dubbo jar包中MET-INF/dubbo/internal下,Transporter對應的配置文件爲
Activate
Activate標記適用於另外一種動態配置
一個接口有多個實現類,Activate標記在每個實現類的type上,並註明“何時被激活”的條件,如group和key的信息,以及在所有被激活實現類中的排序信息。通過ExtensionLoader的getActivateExtension可以獲取到被激活實現類構成的List。下面以最重要的Filter接口進行說明
接口本身不再有Activate註解,而是在它的實現類上面,如TimeoutFilter
只有在group爲provider時纔會生效
MonitorFilter在provider和consumer兩端都會生效。這裏group、key等條件是通過getActivateExtension的方法參數確定的,group通常是預先設定好的,key可以直接指定,也可以動態由url參數決定。當url中包含特定的key(value可以是任意值)時,對應的實例就會被激活。
ExtensionLoader本身還有很多複雜的實現細節,這裏就不再介紹了。感興趣的讀者可以翻閱源碼深入研究一下。