摘要:如前所說,的目的是獲取一個指定實現類的對象。接下來從子模塊下的包的開始分析先來看這里繼承自類。如沒有擴展點,在擴展點實現調用該方法,并返回結果。前面已經分析過,就是使用讀取文件并緩存的反轉控制,就是從和里面提取對象賦值。
如前所說,Dubbo SPI的目的是獲取一個指定實現類的對象。那么Dubbo是通過什么方式獲取的呢?其實是調用ExtensionLoader.getExtension(String name)實現。
具體實現途徑有三種:
①getExtensionLoader(Class
②getAdaptiveExtension() 獲取一個擴展裝飾類的對象,這個類有一個規則,如果它沒有@Adaptive注解,就動態創建一個裝飾類,例如Protocol$Adaptive對象。
③getExtension(String name) 獲取一個指定對象。
(1)分析ExtensionLoader.getExtensionLoader(Class
Dubbo的第一行代碼在哪里?
idea導入Dubbo源碼,在子模塊dubbo-demo-provider/src/test下有DemoProvider.java
package com.alibaba.dubbo.demo.provider; public class DemoProvider { public static void main(String[] args) { com.alibaba.dubbo.container.Main.main(args); } }
這里便是代碼的入口。
這里調到com.alibaba.dubbo.container.Main.java
package com.alibaba.dubbo.container; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; import com.alibaba.dubbo.common.utils.ConfigUtils; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; /** * Main. (API, Static, ThreadSafe) * * @author william.liangf */ public class Main { public static final String CONTAINER_KEY = "dubbo.container"; public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook"; private static final Logger logger = LoggerFactory.getLogger(Main.class); private static final ExtensionLoaderloader = ExtensionLoader.getExtensionLoader(Container.class); private static volatile boolean running = true; public static void main(String[] args) { try { if (args == null || args.length == 0) { String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName()); args = Constants.COMMA_SPLIT_PATTERN.split(config); } final List containers = new ArrayList (); for (int i = 0; i < args.length; i++) { containers.add(loader.getExtension(args[i])); } logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce."); if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { for (Container container : containers) { try { container.stop(); logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!"); } catch (Throwable t) { logger.error(t.getMessage(), t); } synchronized (Main.class) { running = false; Main.class.notify(); } } } }); } for (Container container : containers) { container.start(); logger.info("Dubbo " + container.getClass().getSimpleName() + " started!"); } System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!"); } catch (RuntimeException e) { e.printStackTrace(); logger.error(e.getMessage(), e); System.exit(1); } synchronized (Main.class) { while (running) { try { Main.class.wait(); } catch (Throwable e) { } } } } }
可以看到,Main類中定義了一系列的靜態成員變量,其中:
private static final ExtensionLoaderloader = ExtensionLoader.getExtensionLoader(Container.class);
在Main類初始化階段調用了上述第①條方式為Container創建擴展點。
通過斷點跟進getExtensionLoader方法,會進行new ExtensionLoader
private ExtensionLoader(Class> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
可以看到,這里會進一步調用getExtensionLoader方法,只是這次傳入的是ExtensionFactory.class。通過上面的代碼知道,等價于如下:
this.type = type; objectFactory = null;
執行以上代碼完成了2個屬性的初始化:
1.每個ExtensionLoader都包含了2個值: type 和 objectFactory
Class> type;//構造器初始化時要得到的接口名
ExtensionFactory objectFactory//構造器初始化時設置為AdaptiveExtensionFactory,Dubbo內部默認的實現是SpiExtensionFactory和SpringExtensionFactory。
2.new 一個ExtensionLoader 存儲在ConcurrentMap
關于objectFactory
1.objectFactory就是ExtensionFactory,它也是通過ExtensionLoader.getExtensionLoader(ExtensionFactory.class)來實現的,但是它的objectFactory=null
2.objectFactory作用,它就是為dubbo的IOC提供所有對象。
(2)分析getAdaptiveExtension()
為什么要設計Adaptive?
Adaptive注解在類和方法上有什么區別?
①注解在類上,代表人工實現編碼,即實現了一個裝飾類,如ExtensionFactory。
②注解在方法上,代表自動生成和編譯一個動態的adaptive類,如Protocol$Adaptive。
接下來從子模塊dubbo-config-spring下的schema包的DubboNamespaceHandler開始分析:
package com.alibaba.dubbo.config.spring.schema; import com.alibaba.dubbo.common.Version; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.ModuleConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.AnnotationBean; import com.alibaba.dubbo.config.spring.ReferenceBean; import com.alibaba.dubbo.config.spring.ServiceBean; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
先來看
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
這里ServiceBean繼承自ServiceConfig類。
public class ServiceConfigextends AbstractServiceConfig { private static final long serialVersionUID = 3033787999037024738L; private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); .... }
在這里通過getAdaptiveExtension()獲取protocol。
-->getAdaptiveExtension()//為cachedAdaptiveInstance賦值 -->createAdaptiveExtension() -->getAdaptiveExtensionClass()//該方法看出,如果是預定義的類就直接返回,不然動態生成適配類 -->getExtensionClasses()//為cachedClasses 賦值 -->loadExtensionClasses() -->loadFile(..) -->createAdaptiveExtensionClass()//自動生成和編譯一個動態的adpative類,這個類是一個代理類 -->ExtensionLoader.getExtensionLoader (com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension() -->compiler.compile(code, classLoader) -->injectExtension()//作用:進入IOC的反轉控制模式,實現了動態注入
loadFile(..)方法的作用:把SPI配置文件(如META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol)的內容,存儲在緩存變量里。使用了四個緩存變量。
①緩存包含Adaptive注解的類
cachedAdaptiveClass 如果這個Class含有adaptive注解就賦值進去,如ExtensionFactory有,而Protocol沒有。
②緩存無Adaptive注解的封裝類
cachedWrapperClasses 只有當該class無adaptive注解,并且構造方法參數為目標接口(type,如Protocol)類型,如Protocol里的SPI就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中,如下例:
public class ProtocolFilterWrapper implements Protocol { private final Protocol protocol; public ProtocolFilterWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } 。。。 }
③cachedActivates 剩下的包含Activate注解的類
④cachedName 剩下的類存儲在該map中
在loadExtensionClasses()方法中,有三處loadFile()加載SPI文件:
private Map> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if (value != null && (value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) cachedDefaultName = names[0]; } } Map > extensionClasses = new HashMap >(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
這里的三處loadFile()實際上起到真正作用的是第一個:路徑為META-INF/dubbo/internal/,這個打開dubbo.jar即可看到,這里仍然看com.alibaba.dubbo.rpc.Protocol這個SPI文件:
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper mock=com.alibaba.dubbo.rpc.support.MockProtocol injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol com.alibaba.dubbo.rpc.protocol.http.HttpProtocol com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
上面執行compile時,框架會自動生成如下Protocol$Adpative類代碼:
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException( "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException( "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Exporter export( com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException( "com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException( "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url .getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.export(arg0);//自己執行自己,說明當前類是一個代理類 } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url .getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.refer(arg0, arg1);//自己執行自己,說明當前類是一個代理類 } }
其實就是根據如下模板生成的:
package <擴展點接口所在包>; public class <擴展點接口名>$Adpative implements <擴展點接口> { public <有@Adaptive注解的接口方法>(<方法參數>) { if(是否有URL類型方法參數?) 使用該URL參數 else if(是否有方法類型上有URL屬性) 使用該URL屬性 #if(獲取的URL == null) { throw new IllegalArgumentException("url == null"); } 根據@Adaptive注解上聲明的Key的順序,從URL獲致Value,作為實際擴展點名。 如URL沒有Value,則使用缺省擴展點實現。如沒有擴展點, throw new IllegalStateException("Fail to get extension"); 在擴展點實現調用該方法,并返回結果。 } public <有@Adaptive注解的接口方法>(<方法參數>) { throw new UnsupportedOperationException("is not adaptive method!"); } }
總結起來,Dubbo的所有對象都是通過ExtensionLoader獲取的,SPI是內核。
(3)分析getExtension(String name)
為了進一步分析代理類的擴展類對象生成過程,將Protocol$Adpative類手動創建到dubbo源碼子模塊dubbo-demo下的dubbo-demo-provider中,test目錄下新建包com.alibaba.dubbo.rpc。然后將上述代碼拷貝其中。
然后在getExtension(extName)這里設置斷點:
通過斷點跟蹤,調用鏈如下:
-->getExtension(String name) //指定對象緩存在cachedInstances;get出來的對象可能是wrapper對象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一個。 -->createExtension(String name) -->getExtensionClasses() //前面已經分析過,就是使用loadFile讀取文件并緩存 -->injectExtension(T instance)//dubbo的IOC反轉控制,就是從spi和spring里面提取對象賦值。 -->objectFactory.getExtension(pt, property)//通過ExtensionFactory獲取extension,有兩種 -->①SpiExtensionFactory.getExtension(type, name) -->ExtensionLoader.getExtensionLoader(type) -->loader.getAdaptiveExtension() -->②SpringExtensionFactory.getExtension(type, name) -->context.getBean(name) -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的簡單設計,這個地方如果前面的wrapperClasses緩存不空,那么就會執行這句代碼,如Protocol中只有Filter和Listener,通過使用ProtocolFilterWrapper或ProtocolListenerWrapper的構造方法反射然后注入
通過上述分析,總結起來SPI getExtension()的執行流程及設計模式如下:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68533.html
摘要:二注解該注解為了保證在內部調用具體實現的時候不是硬編碼來指定引用哪個實現,也就是為了適配一個接口的多種實現,這樣做符合模塊接口設計的可插拔原則,也增加了整個框架的靈活性,該注解也實現了擴展點自動裝配的特性。 Dubbo擴展機制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對dubbo整個項目大體的介紹,而從這篇文章開始,我將會從源碼來解讀dubbo再各個模塊的實...
摘要:對于這個矛盾的問題,通過自適應拓展機制很好的解決了。自適應拓展機制的實現邏輯比較復雜,首先會為拓展接口生成具有代理功能的代碼。 1、背景 在 Dubbo 中,很多拓展都是通過 SPI 機制進行加載的,比如 Protocol、Cluster、LoadBalance 等。有時,有些拓展并不想在框架啟動階段被加載,而是希望在拓展方法被調用時,根據運行時參數進行加載。這聽起來有些矛盾。拓展未被...
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...
摘要:引語平時倒是聽得很多又是啥別急我們來先看看面向接口編程的調用關系,來了解一下,和的相似和不同之處。理解先來一段官話的介紹全稱為,是內置的一種服務提供發現機制聽了一臉懵逼好的,我們結合圖片來理解一下。然后調用了內部類的方法。 引語 平時API倒是聽得很多?SPI又是啥.別急我們來先看看面向接口編程的調用關系,來了解一下,API和SPI的相似和不同之處。 SPI理解 先來一段官話的介紹:S...
閱讀 1706·2021-11-24 09:39
閱讀 2489·2021-11-18 10:07
閱讀 3668·2021-08-31 09:40
閱讀 3341·2019-08-30 15:44
閱讀 2637·2019-08-30 12:50
閱讀 3658·2019-08-26 17:04
閱讀 1435·2019-08-26 13:49
閱讀 1271·2019-08-23 18:05