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)

  

posted on 2021-08-13 20:04  偶尔发呆  阅读(127)  评论(0编辑  收藏  举报