dubbo 通过 SPI + ExtensionLoader 实现了一种比较灵活的定制开发的方式,SPI 即配置文件,文件名为接口类的全名,内容为 "key=接口实现类的全名",而 ExtensionLoader 是一个类似于 spring 的容器,在 dubbo 项目中需要获取一个类的实例时,直接调用 ExtensionLoader 的 api 获取,同时它也会为对象注入属性。
用下面代码,描述获取一个 SPI 接口的实例,首先指定接口名获取 ExtensionLoader,然后通过 ExtensionLoader 指定扩展名获取接口的实现类实例。
// org.apache.dubbo.config.ReferenceConfig#postProcessAfterScopeModelChanged @Override protected void postProcessAfterScopeModelChanged() { super.postProcessAfterScopeModelChanged(); protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension(); proxyFactory = this.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); } // org.apache.dubbo.config.ReferenceConfig#createInvokerForRemote invoker = protocolSPI.refer(interfaceClass, urls.get(0));
接口通过 @SPI 注解声明为 SPI 接口,同时注解中指定了默认的扩展名。
而接口方法则通过 @Adaptive 注解声明为 adaptive 方法,adaptive 方法从 @Adaptive 中获取扩展名的 key,如果没有指定 key,则 key 为接口名的首字母小写以句号连接,知道了 key,则在 URL 中获得 key 对应的扩展名。
@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK) public interface Protocol { int getDefaultPort(); @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; // 注解没有指定 key 名,则 key 为 protocol,Protocol$Adaptive 类从 URL 的 protocol 参数获取扩展名,然后通过 // 扩展名获取扩展 @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; }
getAdaptiveExtension 方法动态生成的代码如下:
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.Protocol.class); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) scopeModel.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } }
ExtensionLoader 的方法:
// 通过接口名,加载 SPI 的配置文件,加载该接口对应的所有实现类对象 public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) // 获取接口的 Adaptive 类对象,在 Adaptive 类中通过 URL 的参数传递扩展名 public T getAdaptiveExtension() // 指定扩展名获取指定接口实现类的对象 public T getExtension(String name) public T getExtension(String name, boolean wrap)