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:
- 如果指定了
@Adaptive
的value
,根据此value
寻找 URL 上的参数 - 如果接口方法上未指定
@Adaptive
的value
,会根据类名称按照规则指定,例如HelloService
默认为hello.service
- 如果 URL 的参数上没有
@Adaptive
指定的 key ,使用@SPI
的value
作为默认