文章目录
前言
SPI全称为Service Provider Interface,是一种服务发现机制。在Dubbo中,SPI机制被广泛应用于加载和扩展各种组件,如协议(Protocol)、负载均衡(LoadBalance)、注册中心(Registry)等。Dubbo并未直接使用Java原生的SPI机制,而是对其进行了增强,以满足更复杂和灵活的需求。
Dubbo 加载扩展的整个流程如下:
一、配置文件目录
-
META-INF/services/:
用途:这个目录下的SPI配置文件主要用于兼容JDK原生的SPI机制。 -
META-INF/dubbo/:
用途:这个目录专门用于存放用户自定义的Dubbo SPI配置文件,开发应用的存放目录。格式为文件名为全路径名了,内容采用键值对(Key-Value,简称KV)的格式,其中key被称为扩展名(ExtensionName),value则是对应的具体实现类。
例如
目录:META-INF/dubbo/org.apache.dubbo.rpc.Protocol
内容:http=com.sjl.HttpProtocol -
META-INF/dubbo/internal/:
用途:这个目录用于存放Dubbo内部使用的SPI配置文件
二、标准SPI
// "one" 是默认拓展实现类
@SPI("one")
public interface BaseSpi {
void execute();
}
spi实现
public class OneBaseSpi implements BaseSpi{
@Override
public void execute() {
System.out.println("one base spi");
}
}
public class TwoBaseSpi implements BaseSpi{
@Override
public void execute() {
System.out.println("two base spi");
}
}
配置
one=org.sjl.spi.base.OneBaseSpi
two=org.sjl.spi.base.TwoBaseSpi
测试
@Component
public class BaseTest implements ApplicationRunner {
@Override
public void run(ApplicationArguments args){
ExtensionLoader<BaseSpi> extensionLoader = ExtensionLoader.getExtensionLoader(BaseSpi.class);
// 获取key为 one 的拓展实现类
BaseSpi one = extensionLoader.getExtension("one");
one.execute();
// 获取key为 two 的拓展实现类
BaseSpi two = extensionLoader.getExtension("two");
two.execute();
// 获取 spi 注解上的默认拓展实现类
BaseSpi def = extensionLoader.getDefaultExtension();
def.execute();
}
}
测试结果
三、自适应SPI(Adaptive SPI)
1、@Adaptive说明
1.1、方法级别的注解:
- @Adaptive 注解标记在接口方法上时,Dubbo 会在初始化扩展点时自动生成一个动态代理类,这个代理类会包含被 @Adaptive 注解修饰的方法的代理实现。这些代理实现会根据运行时传入的参数(URL中的参数)来动态选择并调用合适的实现类。URL可以直接作为入参,或者作为入参的入参如org.apache.dubbo.rpc.Invoker
- @Adaptive 注解中的 String[] value()
- 没有填写时,将接口名转换为小写并使用点(.)分隔,然后从 URL 中查找对应的参数值,例如org.sjl.spi.Adaptive.AdaptiveSpi,则对应的key为adaptive.spi
- 填写时,则通过value的值作为key
- value数组等于1
例如@Adaptive(“ADP”)
则从url的取值为
url.getParameter(“ADP”); - value数组大于1
例如@Adaptive(“ADP”,“DDD”)
则从url的取值为
url.getParameter(“ADP”, url.getParameter(“DDD”));
- value数组等于1
1.2、 类级别的注解:
- @Adaptive 注解标记在类上时,该类通常会被直接作为自适应实现类来进行代理。
2、@Adaptive作用在类上
spi接口
@SPI("one")
public interface AdaptiveClassSpi {
void execute(String name);
}
spi实现类
public class OneAdaptiveClassSpi implements AdaptiveClassSpi{
public void execute(String name){
System.out.println("One Adaptive Class Spi");
}
}
public class TwoAdaptiveClassSpi implements AdaptiveClassSpi{
public void execute(String name){
System.out.println("Two Adaptive Class Spi");
}
}
自定义代理类
@Adaptive
public class AdaptiveClassSpiProxy implements AdaptiveClassSpi {
private static final ExtensionLoader<AdaptiveClassSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveClassSpi.class);
public void execute(String name) {
AdaptiveClassSpi adaptiveClassSpi = null;
if (StringUtils.isBlank(name) || !extensionLoader.hasExtension(name)) {
adaptiveClassSpi = extensionLoader.getDefaultExtension();
} else {
adaptiveClassSpi = extensionLoader.getExtension(name);
}
adaptiveClassSpi.execute(name);
}
}
配置
one=org.sjl.spi.Adaptive.OneAdaptiveClassSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveClassSpi
adaptive=org.sjl.spi.Adaptive.AdaptiveClassSpiProxy
测试
@Component
public class AdaptiveClassSpiTest implements ApplicationRunner {
private static final ExtensionLoader<AdaptiveClassSpi> adaptiveClassSpi = ExtensionLoader.getExtensionLoader(AdaptiveClassSpi.class);
@Override
public void run(ApplicationArguments args) throws Exception {
AdaptiveClassSpi adaptiveExtension = adaptiveClassSpi.getAdaptiveExtension();
adaptiveExtension.execute("one");
adaptiveExtension.execute("two");
}
}
结果
3、@Adaptive作用在方法上
spi接口
@SPI("one")
public interface AdaptiveSpi {
public static final String ADP = "ADP";
@Adaptive(ADP)
void execute(URL url);
}
spi实现类
public class OneAdaptiveSpi implements AdaptiveSpi{
public void execute(URL url){
System.out.println("One Adaptive Spi");
}
}
public class TwoAdaptiveSpi implements AdaptiveSpi{
public void execute(URL url){
System.out.println("Two Adaptive Spi");
}
}
配置
one=org.sjl.spi.Adaptive.OneAdaptiveSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveSpi
测试
@Component
public class AdaptiveSpiTest implements ApplicationRunner {
private static final ExtensionLoader<AdaptiveSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveSpi.class);
@Override
public void run(ApplicationArguments args) throws Exception {
// 获取自适应代理类 AdaptiveSpi$Adaptive
AdaptiveSpi adaptiveExtension = extensionLoader.getAdaptiveExtension();
URL url = buildURL();
// 没有对应ADP参数,则取默认spi
adaptiveExtension.execute(url);
URL url1 = buildURL().addParameter(AdaptiveSpi.ADP, "two");
// 取key为ADP的参数的spi
adaptiveExtension.execute(url1);
}
public static URL buildURL() {
URL url = new URL(null, "127.0.0.1", 8090);
return url;
}
}
生成的代理类
public class AdaptiveSpi$Adaptive implements org.sjl.spi.Adaptive.AdaptiveSpi {
public void execute(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("ADP", "one");
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.sjl.spi.Adaptive.AdaptiveSpi) name from url (" + url.toString() + ") use keys([ADP])");
ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.sjl.spi.Adaptive.AdaptiveSpi.class);
org.sjl.spi.Adaptive.AdaptiveSpi extension = (org.sjl.spi.Adaptive.AdaptiveSpi) scopeModel.getExtensionLoader(org.sjl.spi.Adaptive.AdaptiveSpi.class).getExtension(extName);
extension.execute(arg0);
}
}
结果
三、激活SPI(Activate SPI)
Dubbo 的激活点机制基于 @Activate 注解完成,可以用于实现根据条件加载多个 SPI 激活点实现类。
- @Activate 注解:
- String[] group() default {}:
- getActivateExtension 接口传入的 group 参数为 null 或者 length==0,表示不限制 group;
- @Activate 注解中的参数 groups 是否包含传入的限制参数 group,如果包含,则允许加载当前的 SPI 实现。
- String[] value() default {}:
- @Activate 注解没有 value() 属性,则默认所有值不包括空;
- URL#getParameters() 中的一个参数名,则认为默认是允许当前的 SPI 实现加载的
- int order() default 0;
- 表示加载顺序,数字越小越靠前
spi接口
- 表示加载顺序,数字越小越靠前
- String[] group() default {}:
@SPI
public interface ActivateSpi {
void execute();
}
spi实现
@Activate(value = "one",group = {"active","active2"}, order = 1)
public class OneActivateSpi implements ActivateSpi{
@Override
public void execute() {
System.out.println("One Activate Spi");
}
}
@Activate(value = "two",group = "active", order = 2)
public class TwoActivateSpi implements ActivateSpi{
@Override
public void execute() {
System.out.println("Two Activate Spi");
}
}
@Activate(group = "active", order = 3)
public class ThreeActivateSpi implements ActivateSpi{
@Override
public void execute() {
System.out.println("Three Activate Spi");
}
}
@Activate(order = 4, value = "four")
public class FourActivateSpi implements ActivateSpi{
@Override
public void execute() {
System.out.println("Four Activate Spi");
}
}
配置
one=org.sjl.spi.Activate.OneActivateSpi
two=org.sjl.spi.Activate.TwoActivateSpi
three=org.sjl.spi.Activate.ThreeActivateSpi
four=org.sjl.spi.Activate.FourActivateSpi
测试
@Component
public class ActiveSpiTest implements ApplicationRunner {
private static final ExtensionLoader<ActivateSpi> extensionLoader = ExtensionLoader.getExtensionLoader(ActivateSpi.class);
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("仅指定 group");
String group = "active";
// 只会获取到仅设置了group为active 且 value值不填写的
List<ActivateSpi> activateSpiList = extensionLoader.getActivateExtension(buildURL(), new String[]{}, group);
activateSpiList.forEach(ActivateSpi::execute);
System.out.println("指定 group 和 value");
// 获取到仅设置了group为active 且 value值不填写的或者value为one的
List<ActivateSpi> activateSpiList1 = extensionLoader.getActivateExtension(buildURL(), new String[]{"one"}, group);
activateSpiList1.forEach(ActivateSpi::execute);
System.out.println("指定 value");
// 获取到设置了group的所有值或者不设置 且 value需要等于four或者不填的
List<ActivateSpi> activateSpiList2 = extensionLoader.getActivateExtension(buildURL(), new String[]{"four"}, null);
activateSpiList2.forEach(ActivateSpi::execute);
}
public static URL buildURL() {
URL url = new URL(null, "127.0.0.1", 8090);
return url;
}
}
测试结果
四、Dubbo AOP
AOP 增强,通常以Wrapper结尾,且需要实现如下协议
- wrapper 类也必须实现 SPI 接口
- wrapper 类必须有一个含有单个 SPI 参数的构造器
针对上面的 AdaptiveSpi ,我们增加AOP强化功能
增强类
public class AdaptiveSpiWrapper implements AdaptiveSpi {
private AdaptiveSpi adaptiveSpi;
public AdaptiveSpiWrapper(AdaptiveSpi adaptiveSpi){
this.adaptiveSpi = adaptiveSpi;
}
@Override
public void execute(URL url) {
System.out.println("@AOP(order = 100)");
adaptiveSpi.execute(url);
}
}
public class AdaptiveSpiWrapper2 implements AdaptiveSpi {
private AdaptiveSpi adaptiveSpi;
public AdaptiveSpiWrapper2(AdaptiveSpi adaptiveSpi){
this.adaptiveSpi = adaptiveSpi;
}
@Override
public void execute(URL url) {
System.out.println("@AOP(order = -100)");
adaptiveSpi.execute(url);
}
}
配置
- 配置的先后顺序,代表AOP的先后顺序
one=org.sjl.spi.Adaptive.OneAdaptiveSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveSpi
wrapper2=org.sjl.spi.aop.AdaptiveSpiWrapper2
wrapper=org.sjl.spi.aop.AdaptiveSpiWrapper
测试
@Component
public class AdaptiveSpiTest implements ApplicationRunner {
private static final ExtensionLoader<AdaptiveSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveSpi.class);
@Override
public void run(ApplicationArguments args) throws Exception {
AdaptiveSpi adaptiveExtension = extensionLoader.getAdaptiveExtension();
URL url = buildURL();
// 此时的链路为 AdaptiveSpiWrapper2 -> AdaptiveSpiWrapper -> OneAdaptiveSpi
adaptiveExtension.execute(url);
URL url1 = buildURL().addParameter(AdaptiveSpi.ADP, "two");
// 此时的链路为 AdaptiveSpiWrapper2 -> AdaptiveSpiWrapper -> TwoAdaptiveSpi
adaptiveExtension.execute(url1);
}
public static URL buildURL() {
URL url = new URL(null, "127.0.0.1", 8090);
return url;
}
}
结果
五、Dubbo IOC
Dubbo IOC 的实现机制
- setter 方法注入依赖。
- 注入为spring bean 或者 自适应扩展点
针对上面的代码,在OneBaseSpi 依赖注入 AdaptiveSpi
public class OneBaseSpi implements BaseSpi{
private AdaptiveSpi adaptiveSpi;
public void setAdaptiveSpi(AdaptiveSpi adaptiveSpi) {
this.adaptiveSpi = adaptiveSpi;
}
@Override
public void execute() {
adaptiveSpi.execute(buildURL());
System.out.println("one base spi");
}
public static URL buildURL() {
URL url = new URL(null, "127.0.0.1", 8090);
return url;
}
}
配置保存不变
one=org.sjl.spi.base.OneBaseSpi
测试
@Component
public class BaseTest implements ApplicationRunner {
@Override
public void run(ApplicationArguments args){
ExtensionLoader<BaseSpi> extensionLoader = ExtensionLoader.getExtensionLoader(BaseSpi.class);
BaseSpi one = extensionLoader.getExtension("one");
one.execute();
}
}
结果