dubbo SPI 主要配置技术解读

时间:2022-07-28
本文章向大家介绍dubbo SPI 主要配置技术解读,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

所谓SPI就是接口由框架定义,具体实现可以有不同供应商提供不同的实现。

dubbo在JDK基础上对SPI做了改进和扩展。

dubbo 的SPI 不但实现了实现类的动态加载,还实现了类似spring 的IOC,AOP的功能

本文就上述功能讲下具体使用方法

基本SPI 配置

dubbo 源码包的有些模块的 META-INF/dubbo/目录下 有以接口名命名的文件,里面有是 name=类全面形式的内容

比如

META-INF/dubbo/org.apache.dubbo.rpc.Protocol 文件的内容有
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

DubboProtocol的实现类是dubbo默认的调用协议实现

这样就可以在我们使用dubbo的配置文件中,通过 name 指定我们用的Protocol具体哪个实现

<dubbo:protocol name="dubbo"/>

同样你可以添加自己的实现类,比如x.y.z.XXXProtocol 让后在META-INF/dubbo/org.apache.dubbo.rpc.Protocol 文件中添加 新的一行 xxx=x.y.z.XXXProtocol 然后就可以在配置中使用了,这就是最基本的SPI功能。

IOC功能

dubbo不只是还实现了依赖注入的功能,比如DubboProtocol 先类种有个属性filter

public class DubboProtocol extends AbstractProtocol {
    public Filter filter;
    public void setFilter(Filter filter) {
        this.filter = filter;
    }
...
}

Filter 本身也一个接口,可以是@SPI接口,也可以是spring的一个bean

dubbo也可以自动从dubbo 扩展或者spring容器中找到实现类型自动注入

有个问题,如果dubbo org.apache.dubbo.rpc.Filter的文件中有多个实现配置,比如

mockfilter=org.apache.dubbo.config.mock.MockFilte
orc=org.apache.dubbo.config.orc.OrcFilte
mov=org.apache.dubbo.config.mov.MovFilter

应该用哪一个类呢?这就要用到Adaptive注解,比如

@Adaptive
public class MockFilter implements Filter {}

这个就表示用 MockFilter类作为实现类

关于@Adaptive注解,它还能写在接口方法上,如果不在类上指定dubbo 会为这个接口自动生成一个 接口名$Adpative 命名的实现类。比如 dubbo Protocol接口 refer方法

@SPI("dubbo")
public interface Protocol {
    @Adaptive //没在类中指定,写在了方法上
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
.....
}

dubbo 动态生成的Adapatvie类和refer方法实现如下:

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
   .....
    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);
    }
......
}

可以看到,它实现的功能就是通过调用时url中的参数指定具体哪个实现类。

AOP

AOP就动态方法包装,完成切面编程,dubbo是通过 wrapper类实现的。

还是文件org.apache.dubbo.rpc.Protocol 文件,里面有这些配置

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrappe
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrappe
mock=org.apache.dubbo.rpc.support.MockProtocol

其中 ProtocolFilterWrapper就是Wrapper类,并不是因为类名有Wrapper,而是由于实现中有Protocol 接口类型的构造函数

@Activate(order = 100)
public class ProtocolFilterWrapper implements Protocol {
    private final Protocol protocol;
    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
...
   //对refer实现了包装
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (UrlUtils.isRegistry(url)) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
    }
}
//另外一个Wrappe
@Activate(order = 200)
public class ProtocolListenerWrapper implements Protocol {
    private final Protocol protocol;
    public ProtocolListenerWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
...
}

想必你注意到了@Activate(order = 200)注解,它指定多个wrapper 的排序,通过@Activate的order属性排序。

Activate

比如常用的Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等实现集合,我们可以通过 Activate注解完成多个实现类的一次加载和激活,还能配置条件激活,比如

@Activate // 任何条件下激活
public class XxxFilter implements Filter {
    // ...
}
@Activate(group = "provider", value = "cache") //这个就是在group是“provider”  value 等于cache 下激活
public class CacheFilter  implements Filter {
    // ...
}

这个功能的实现可看 ExtensionLoader类的getActivateExtension方法: