20211012 Dubbo 的 SPI 和 Adaptive

准备工作

定义服务接口:

package com.lagou.service;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

@SPI("human")
public interface HelloService {
    String sayHello();

    @Adaptive
    String sayHello(URL url);
}

两个接口实现类:

package com.lagou.service.impl;

import com.lagou.service.HelloService;
import org.apache.dubbo.common.URL;

public class DogHelloService implements HelloService{
    @Override
    public String sayHello() {
        return "wang wang";
    }

    @Override
    public String sayHello(URL url) {
        return "wang url";
    }
}
package com.lagou.service.impl;

import com.lagou.service.HelloService;
import org.apache.dubbo.common.URL;

public class HumanHelloService implements HelloService{
    @Override
    public String sayHello() {
        return "hello 你好";
    }

    @Override
    public String sayHello(URL url) {
        return  "hello url";
    }
}

SPI

定义 SPI 配置文件:META-INF/dubbo/com.lagou.service.HelloService

human=com.lagou.service.impl.HumanHelloService
dog=com.lagou.service.impl.DogHelloService

SPI 调用:

package com.lagou;

import com.lagou.service.HelloService;
import org.apache.dubbo.common.extension.ExtensionLoader;

import java.util.Set;

public class DubboSpiMain {
    public static void main(String[] args) {
        // 获取扩展加载器
        ExtensionLoader<HelloService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloService.class);
        // 遍历所有的支持的扩展点 META-INF.dubbo
        Set<String> extensions = extensionLoader.getSupportedExtensions();
        for (String extension : extensions) {
            String result = extensionLoader.getExtension(extension).sayHello();
            System.out.println(result);
        }

        System.out.println("=================");

        // 指定调用
        System.out.println(extensionLoader.getExtension("human").sayHello());

    }
}

Adaptive

public class DubboAdaptiveMain {
    public static void main(String[] args) {
        URL url = URL.valueOf("test://localhost/hello?hello.service=dog");
        ExtensionLoader<HelloService> extensionLoader = ExtensionLoader.getExtensionLoader(HelloService.class);
        HelloService adaptiveExtension = extensionLoader.getAdaptiveExtension();
        String msg = adaptiveExtension.sayHello(url);
        System.out.println(msg);
    }
}

分析源码

生成 Adaptive 类代码:org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass

以上面的接口为例,生成的代码是:

package com.lagou.service;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class HelloService$Adaptive implements com.lagou.service.HelloService {
    public java.lang.String sayHello() {
        throw new UnsupportedOperationException(
                "The method public abstract java.lang.String com.lagou.service.HelloService.sayHello() of interface com.lagou.service.HelloService is not adaptive method!");
    }

    public java.lang.String sayHello(org.apache.dubbo.common.URL arg0) {
        if (arg0 == null)
            throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("hello.service", "human");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (com.lagou.service.HelloService) name from url ("
                    + url.toString() + ") use keys([hello.service])");
        com.lagou.service.HelloService extension = (com.lagou.service.HelloService) ExtensionLoader
                .getExtensionLoader(com.lagou.service.HelloService.class).getExtension(extName);
        return extension.sayHello(arg0);
    }
}

可以看出,Adaptive 类只是做了一个转发,实际还是 SPI 调用,这里转发的工作是获得 SPI 的 key ,也就是按照一定的规则选择 SPI:

  • 如果指定了 @Adaptivevalue ,根据此 value 寻找 URL 上的参数
  • 如果接口方法上未指定 @Adaptivevalue ,会根据类名称按照规则指定,例如 HelloService 默认为 hello.service
  • 如果 URL 的参数上没有 @Adaptive 指定的 key ,使用 @SPIvalue 作为默认
posted @ 2021-10-12 09:38  流星<。)#)))≦  阅读(45)  评论(0编辑  收藏  举报