所谓SPI就是接口由框架定义,具体实现可以有不同供应商提供不同的实现。
dubbo在JDK基础上对SPI做了改进和扩展。
dubbo 的SPI 不但实现了实现类的动态加载,还实现了类似spring 的IOC,AOP的功能
本文就上述功能讲下具体使用方法
基本SPI 配置
dubbo 源码包的有些模块的 META-INF/dubbo/目录下 有以接口名命名的文件,里面有是 name=类全面形式的内容
比如
代码语言:javascript复制META-INF/dubbo/org.apache.dubbo.rpc.Protocol 文件的内容有
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
DubboProtocol的实现类是dubbo默认的调用协议实现
这样就可以在我们使用dubbo的配置文件中,通过 name 指定我们用的Protocol具体哪个实现
代码语言:javascript复制<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
代码语言:javascript复制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的文件中有多个实现配置,比如
代码语言:javascript复制mockfilter=org.apache.dubbo.config.mock.MockFilte
orc=org.apache.dubbo.config.orc.OrcFilte
mov=org.apache.dubbo.config.mov.MovFilter
应该用哪一个类呢?这就要用到Adaptive注解,比如
代码语言:javascript复制@Adaptive
public class MockFilter implements Filter {}
这个就表示用 MockFilter类作为实现类
关于@Adaptive注解,它还能写在接口方法上,如果不在类上指定dubbo 会为这个接口自动生成一个 接口名$Adpative 命名的实现类。比如 dubbo Protocol接口 refer方法
代码语言:javascript复制@SPI("dubbo")
public interface Protocol {
@Adaptive //没在类中指定,写在了方法上
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
.....
}
dubbo 动态生成的Adapatvie类和refer方法实现如下:
代码语言:javascript复制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 文件,里面有这些配置
代码语言:javascript复制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 接口类型的构造函数
代码语言:javascript复制@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注解完成多个实现类的一次加载和激活,还能配置条件激活,比如
代码语言:javascript复制@Activate // 任何条件下激活
public class XxxFilter implements Filter {
// ...
}
@Activate(group = "provider", value = "cache") //这个就是在group是“provider” value 等于cache 下激活
public class CacheFilter implements Filter {
// ...
}
这个功能的实现可看 ExtensionLoader类的getActivateExtension方法: