DUBBO SPI

Dubbo 的可扩展性是基于 SPI 去实现的,而且Dubbo所有的组件都是通过 SPI 机制加载。

SPI 全称为 (Service Provider Interface) ,是一种服务提供发现机制。可以将服务接口与服务实现分离以达到解耦可拔插、大大提升了程序可扩展性。

一个接口有多个实现类,具体使用哪个实现类,通过SPI机制让用户来决定。

@SPI:此注解表示这是一个SPI接口,标注在Interface上。

配置文件

在指定目录下创建配置文件,文件名格式为接口的全限定名(此处为com.javaedit.spi.Robot)

指定目录有3个,分别为:

META-INF/dubbo/internal

META-INF/dubbo

META-INF/services

@SPI注解有value参数,可以配置默认实现类的key

// 接口的注解添加默认值
@SPI("norRobot")
// 获取实现类时将getExtension替换一下
// Robot robot = extensionLoader.getExtension("norRobot");
Robot robot = extensionLoader.getDefaultExtension();

@SPI    // 必须添加SPI注解
public interface TestInterface {

    public void say();

}
public class TestImpla implements TestInterface {

    @Override
    public void say() {
        System.out.println("aaaaaaa");
    }
}
public class TestImplb implements TestInterface {

    @Override
    public void say() {
        System.out.println("bbbbbbbbb");
    }

}
配置文件
testaa=example.impl.TestImpla
testbb=example.impl.TestImplb
testcc=example.impl.TestImplc
ExtensionLoader.getExtensionLoader(TestInterface.class).getExtension("testaa").say();

包装类

Dubbo SPI提供了类似装饰器模式的实现

原理就是RobotWrapper只要有构造方法是有且只有一个参数,且这个参数是Robot类型,就认为其实包装类,会自动将Robot通过构造方法注入。

所以getExtension("norRobot")实际返回的是RobotWrapper

如果多个包装类,就按照配置文件里的顺序倒叙执行

@SPI    // 必须添加SPI注解
public interface TestInterface {

    public void say();

}
public class TestImpla implements TestInterface {

    @Override
    public void say() {
        System.out.println("aaaaaaa");
    }
}
public class TestImplWrappera implements TestInterface {

    private TestInterface testInterface;

    public TestImplWrappera(TestInterface testInterface) {
        this.testInterface = testInterface;
    }

    @Override
    public void say() {
        System.out.println("wrappera1");
        testInterface.say();
        System.out.println("wrappera2");
    }
}
public class TestImplWrapperb implements TestInterface {

    private TestInterface testInterface;

    public TestImplWrapperb(TestInterface testInterface) {
        this.testInterface = testInterface;
    }

    @Override
    public void say() {
        System.out.println("wrapperb1");
        testInterface.say();
        System.out.println("wrapperb2");
    }

}
配置文件
testaa=example.impl.TestImpla
wrappera=example.impl.TestImplWrappera
wrapperb=example.impl.TestImplWrapperb
ExtensionLoader.getExtensionLoader(TestInterface.class).getExtension("testaa").say();

执行顺序:b1 a1 aaaa a2 b2

自适应扩展

有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这就是Dubbo SPI自适应扩展的作用。

@Adaptive:此注解用于自适应扩展,可标注在类或者方法上。

1、类的自适应扩展

@Adaptive标注在类上时,无需通过key指定需要获取的实现类,通过getAdaptiveExtension方法即可获取自适应扩展类。同一个接口,有且只能有一个实现类允许使用@Adaptive标注。

@Adaptive
public class TestImplAdaptiveClass1 implements TestInterface {

    @Override
    public void say() {
        System.out.println("adaptive1");
    }
}
配置文件
adative1=example.impl.TestImplAdaptiveClass1
ExtensionLoader.getExtensionLoader(TestInterface.class).getAdaptiveExtension().say();

 // 不用再指定key

如果配置文件里两个自适应扩展类,只有上面一个会生效

adative1=example.impl.TestImplAdaptiveClass1
adative2=example.impl.TestImplAdaptiveClass2

不会触发包装类-自测是的,需要权威验证!!! 

目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory、AdaptiveCompiler 有这么用.

2、方法的自适应扩展

在接口的方法上加注解,不是实现类。@Adaptive标注在类上和标注在方法上是冲突的。有类自适应注解,会屏蔽方法自适应。

会触发包装类-自测是的

@SPI
public interface TestInterface {

    @Adaptive({"aaa"})
    public void say(URL url);

}
public class TestImplAdaptiveMethod1 implements TestInterface {

    @Override
    public void say(URL url) {
        System.out.println("adaptivem1");
    }

}
public class TestImplAdaptiveMethod2 implements TestInterface {

    @Override
    public void say(URL url) {
        System.out.println("adaptivem2");
    }
    
}
配置文件
wrappera=example.impl.TestImplWrappera
wrapperb=example.impl.TestImplWrapperb
adativem1=example.impl.TestImplAdaptiveMethod1
adativem2=example.impl.TestImplAdaptiveMethod2
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("aaa", "adativem1");
        URL url = new URL("", "", 1, map);
        ExtensionLoader.getExtensionLoader(TestInterface.class).getAdaptiveExtension().say(url);
    }
输出:
wrappera1
wrapperb1
adaptivem1
wrapperb2
wrappera2

@Adaptive标注在方法上时,getAdaptiveExtension获取的是动态生成的自适应扩展类,固定类名是 接口名$Adaptive

public class Robot$Adaptive implements com.javaedit.spi.Robot {
    public void sayHello(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        // 从url中获取robotAda参数,也就是extName = "norRobot"
        String extName = url.getParameter("robotAda");
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.javaedit.spi.Robot) name from url(" + url.toString() + ") use keys([robot])");
        // 获取norRobot,也就是RobotImpl类
        com.javaedit.spi.Robot extension = (com.javaedit.spi.Robot) ExtensionLoader.getExtensionLoader(com.javaedit.spi.Robot.class).getExtension(extName);
        // 调用norRobot的sayHello方法
        extension.sayHello(arg0);
    }
}

Robot$Adaptive的sayHello会动态从URL参数中获取实际要调用的Robot实现类,这样就实现了根据运行时参数进行加载的功能。

动态选择实现类,是需要通过URL来传递参数的。也就是方法参数中需要包含URL对象或者方法参数中有getUrl()方法来提供URL对象。

可以设置多个键名( Key ),顺序获取直到有值.若最终获取不到,使用默认拓展名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值