前面用了4 篇文章分析了 Dubbo SPI 的幾種用法以及如何在 Dubbo 中應用的,apache
本文經過調試 Dubbo2.7.x 源碼分析 如何經過 getExtension(name) 獲取一個擴展對象實例 。緩存
回顧一下 Dubbo SPI 的最基本的用法app
public class App { public static void main( String[] args ) { // 第一步 ExtensionLoader<HelloService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloService.class); // 第二步 HelloService helloService = extensionLoader.getExtension("helloService"); // 第三步 helloService.sayHello("xiaoming"); } }
咱們直接從第二步 debug 進入getExtension(name)
方法源碼分析
在 debug 進入方法以前, 先來看幾個 ExtensionLoader
的屬性.net
// 1. 擴展接口, 好比 Protocol private final Class<?> type; // 2. 擴展實現類集合, key 爲 Protocol , value 爲 DubboProtocol private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(); // 3. (緩存的)擴展實現類集合 private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); // 4. 緩存的擴展對象集合 key 爲 dubbo, value 爲 DubboProtocol private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); // 5. 擴展加強 Wrapper 實現類集合 private Set<Class<?>> cachedWrapperClasses;
此方法大體分爲 3 大步:debug
下面就針對上面的 三大步, 逐個分析調試
public T getExtension(String name) { // 省略擴展名非空校驗 // 1. 若是 name 等於 true, 獲取默認的擴展對象 if ("true".equals(name)) { return getDefaultExtension(); } // 2. 從緩存的擴展類對象獲取 final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); // 緩存中沒有對應的實例 if (instance == null) { synchronized (holder) { instance = holder.get(); // 雙重校驗 if (instance == null) { // 3. 加載擴展實現類,並實例化 instance = createExtension(name); // 擴展對象放入緩存 holder.set(instance); } } } return (T) instance; }
public T getDefaultExtension() { // 1.1 獲取全部的擴展類 getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); }
private Map<String, Class<?>> getExtensionClasses() { // 先從緩存中取 // cachedClasses 放置的是緩存的擴展實現類集合 Map<String, Class<?>> classes = cachedClasses.get(); // 依然是雙重校驗+鎖的方法去獲取 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // 緩存沒有命中, 就會去加載 META-INF/dubbo ,META-INF/dubbo/intenal, ,META-INF/service 目錄下去加載 classes = loadExtensionClasses(); // 加載完成,就放入緩存 cachedClasses.set(classes); } } } return classes; }
上面也有提到, 緩存中沒有,就會去下面的目錄去加載文件, 而後解析文件中的內容code
具體源碼就不貼出來了, 文件的內容格式以下:對象
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
private Holder<Object> getOrCreateHolder(String name) { // 跟上面一模一樣, 先從緩存中取, // cachedInstances 放置的是 緩存的擴展類對象 Holder<Object> holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } return holder; }
DubboProtocol
)instance
instance
依賴其餘擴展實現類 OtherClass, 就須要把 OtherClass 實例化,並經過 setter 方法注入到instance
裏面判斷 Protocol 是否有其餘加強實現, 好比 ProtocolFilterWrapper
等等blog
ProtocolFilterWrapper
實例化, 賦值給 instance
private T createExtension(String name) { // 獲取指定的擴展類 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { // 從擴展實現類集合中獲取 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 注入依賴的擴展類對象 injectExtension(instance); // 判斷是不是 wrapper Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { // 從新賦值成一個 wrapper (加強類) instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } initExtension(instance); return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }
本文主要針對經過 getExtension(name)
獲取一個擴展對象實例, 來對 Dubbo 的源碼進行了剖析, 固然全文只是描述了一個大概的流程, 好比如何解析 SPI 配置文件 就沒有深刻去講解.
大致流程以下所示:
"true".equals(name)
), 經過 getDefaultExtension()
方法獲取 默認的擴展接口實現類對象, 並返回判斷緩存 cachedInstances
集合中是否存在
instance
若是沒有, 就經過 createExtension
獲取 擴展接口實現類對象
DubboProtocol
instance
, 若是沒有就把上一步的類DubboProtocol
給實例化instance
是否依賴其餘擴展接口實現類對象 CLassA, CLassB
等等,若是有,須要經過 setter 方法注入進去DubboProtocol
是否有其餘增 Wrapper 實現類, 好比 ProtocolFilterWrapper
, 若是有, 賦值給上面的instance
return instance
下一篇文章會針對 獲取自適應擴展點實例 getAdaptiveExtension()
方法進行源碼分析!