框架基石,DubboSPI机制

Dubbo良好的扩展性与两个方面是密不可分的,一是整 个框架中针对不同的场景,恰到好处地使用了各种设计模式,二就是本章要介绍的加载机制。基于DubboSPI加载机制,让整个框架的接口和具体实现完全解耦,从而奠定了整个框架良好可扩展性的基础。

dubbo的扩展核心类ExtensionLoader可谓框架的基石。可以看到源码中大量引入的ExtensionLoader相关的扩展机制,了解了扩展机制是阅读后续源码的大前提。

ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);

扩展点特性

  • 自动包装Wrapper
  • 自动装配
  • 自适应@Adaptive
  • 自动激活@Activate

先放上一张ExtensionLoader的工作流程图

img

自动包装Wrapper

自动包装扩展点的 Wrapper 类。ExtensionLoader 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。

比如dubbo中常用的包装类

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

**filter:**过滤器调用链包装器;
**listener:**协议监听器包装器;
**qos:**在线运维服务包装器;

通过这些包装类,将listener织入到我们export和reffer的调用链路中,将 filter织入到我们invoke远程调用流程中,具体包装后的链路组装逻辑,可以在之后的文章中阅读源码查看。

public class ProtocolListenerWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolListenerWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
}
public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
}

自动装配

除了在构造函数中传入其他扩展实例,我们还经常使用setter 方法设置属性值。如果某个扩展类是另外-一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例。ExtensionLoader在执行扩展点初始化的时候,会自动通过setter方法注入对应的实现类。这里存在-一个问题,如果扩展类属性是一个接口,它有多种实现,那么具体注入哪一个呢?这就涉及第三个特性自适应。

@SPI
public interface CarMaker {
    Car makeCar();
}
@SPI
public interface WheelMaker {
    Wheel makeWheel();
}
public class RaceCarMaker implements CarMaker {
    WheelMaker wheelMaker;
 
    public void setWheelMaker(WheelMaker wheelMaker) {//这里装配了扩展类
        this.wheelMaker = wheelMaker;
    }
 
    public Car makeCar() {
        // ...
        Wheel wheel = wheelMaker.makeWheel();//具体使用扩展类哪个实现类的方法
        // ...
        return new RaceCar(wheel, ...);
    }
}

自适应@Adaptive

在Dubbo SPI中,我们使用@Adaptive注解,可以动态地通过URL中的参数来确定要使用哪个具体的实现类。从而解决自动加载中的实例注入问题。

@SPI
public interface Transporter {
    @Adaptive({"server", "transport"})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;
 
    @Adaptive({"client", "transport"})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

对于 bind() 方法,Adaptive 实现先查找 server key,如果该 Key 没有值则找 transport key 值,来决定代理到哪个实际扩展点。

@SPI("dubbo")
public interface Protocol {

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}

如果@Adaptive没指定key,默认取类名小写protocol作为key,如果URL中没有protocol参与,那么会取@SPI(“dubbo”)标注的默认key作为value值dubbo。

这种动态寻找实现类的方式比较灵活,但只能激活-一个具体的实现类,如果需要多个实现类同时被激活,如Filter可以同时有多个过滤器;或者根据不同的条件,同时激活多个实现类,如何实现?这就涉及最后一个特性一自动激活。

自动激活@Activate

对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同时加载多个实现,此时,可以用自动激活来简化配置,如:

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
 
@Activate // 无条件自动激活
public class XxxFilter implements Filter {
    // ...
}
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
 
@Activate("cache") // 当配置了xxx参数,并且参数为有效值时激活,比如配了cache="lru",自动激活CacheFilter。
public class XxxFilter implements Filter {
    // ...
}
public static void main(String[] args) {
        ExtensionLoader<Filter> loader = ExtensionLoader.getExtensionLoader(Filter.class);
        URL url = new URL("", "", 0);
        // 在url中添加cache参数
        url = url.addParameter("cache", "cache");
        List<Filter> filters = loader.getActivateExtension(url, "cache");
        System.out.println(filters);
}
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
 
@Activate(group = "provider", value = "xxx") // 只对提供方激活,group可选"provider"或"consumer"
public class XxxFilter implements Filter {
    // ...
}

源码阅读

有了这些前置知识,接下来可以深入查看源码,了解下dubbo是怎么实现这样在运行时动态适应的扩展类的。实际上就是用javassist生成一个新的类,在方法里去做动态判断。

参考资料:

官网扩展点介绍

官网spi源码介绍

官网自适应扩展实现源码

csdn:

深度解析dubbo扩展技术dubbo spi(注解)

深度解析dubbo扩展技术dubbo spi(实现二)

深度解析dubbo扩展技术dubbo spi(自适应实现)

深度解析dubbo扩展技术dubbo spi(自动激活实现)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值