概述
为什么
Dubbo
要自己实现一套自己的SPI
机制呢?
Java SPI
不支持依赖注入,对扩展点的依赖不友好。Dubbo SPI
支持依赖注入,即在实例化扩展点的过程中,通过反射调用扩展点的setXXX
方法,注入依赖的扩展点;Java SPI
获取实现类方式单一,只能通过遍历获取。Dubbo SPI
支持通过key
获取实现类,使用起来更方便、更灵活;Dubbo SPI
还实现了强大的自适应扩展和自动激活功能,通过这两个功能可以实现在运行时替换具体实现类(运行到具体的方法时才决定使用哪个实现)以及简化配置;
基本使用
使用步骤
- 在
jar
包的META-INF/dubbo
下面创建一个接口全限定名的文件; - 接口全限定名文件中以键值对的形式填写实现类;
- 键可以是任意唯一的标识符,值要求是接口实现类的全限定类名;
- 接口添加
@SPI
注解; - 使用
ExtensionLoader.getExtensionLoader(class)
获取ExtensionLoader
对象; - 使用
ExtensionLoader::getExtension(key)
获取接口实现类对象;
例子
在 META-INF/dubbo
下新建一个 UserService
接口全限定名称的文件。
## 声明配置
key1=com.example.impl.UserServiceImpl
key2=com.example.impl.UserServiceImpl0
给接口添加 @SPI
注解。
/**
* 协议接口
*/
@SPI
public interface UserService {
String getHello();
}
// 对应实现com.example.impl.UserServiceImpl和com.example.impl.UserServiceImpl0
代码使用实例
public static void main(String[] args) {
//获取ExtensionLoader对象
ExtensionLoader<UserService> extensionLoader = ExtensionLoader
.getExtensionLoader(UserService.class);
UserService adaptiveExtension = extensionLoader.getExtension("key1");
// 调用的是UserServiceImpl0
adaptiveExtension.getHello();
}
进阶使用——自适应拓展
Dubbo
使用一个 ExtensionLoader
去加载多个接口/抽象类的实现,在不同场景下,同个方法可能需要调用不同的子类实现,所以 Dubbo
提供了一种动态调用子类实现的方式。
注意点:
@Adaptive
注解:用来标注子类、接口方法;- 标注子类,表示该子类用作该该接口自适应调用的默认实现;
- 标注接口方法,表示该接口方法作为自适应实现;
@SPI
注解:它用来某个接口作为SPI
使用,并且value
是自适应方法调用的默认实现;- 要求作为自适应调用的方法,必须有一个参数是
URL
类型,它以GET
请求的方式填充参数,指定@Adaptive
上value
数组的匹配关系;
例子:
## 声明配置
key1=com.example.impl.UserServiceImpl
key2=com.example.impl.UserServiceImpl0
@SPI("key1") // 声明为自适应方法默认key1为key的子类默认实现
public interface UserService {
@Adaptive({"key2", "key1"}) // 声明为自适应方法
String getHello(User user, URL url, String a);
}
public class UserServiceImpl implements UserService {
@Override
public String getHello(User user, URL url, String a) {
System.out.println("UserServiceImpl");
return "UserServiceImpl";
}
}
public class UserServiceImpl0 implements UserService {
@Override
public String getHello(User user, URL url, String a) {
System.out.println("UserServiceImpl0");
return "UserServiceImpl0";
}
}
// 调用
public static void main(String[] args) throws IOException {
// 获取自适应代理类
UserService adaptiveExtension = ExtensionLoader
.getExtensionLoader(UserService.class)
.getAdaptiveExtension();
// 其实URL任意都行,只要是合法的URL就可以
adaptiveExtension.getHello(new User(), URL.valueOf("http://127.0.0.1:1000/1?key1=key1&key2=key2"), "11");
// 最终调用的是key2=UserServiceImpl0
}
- 若
@Adaptive
的value
字段不为空,则实现类查找顺序为@Adaptive
注解的value
数组中的,如果都找不到,才找@SPI
的value
; - 若
@Adaptive
的value
字段为空,则实现类查找顺序为user.service
参数,找不到才去@SPI
的value
; - 若查找不到所需的实现类则会抛出异常;
进阶使用——拓展点的子类激活
SPI
机制正常情况迭代完后,可以获取所有的实现子类,但是有些场景下,我们只需要激活部分子类实现即可,而不需要激活全部的子类,即通过一定的条件去过滤掉不满足条件的子类,返回匹配的子类集合。
@Activate
注解是实现配置化激活的关键,用来标注接口的实现类,在满足场景的情况下才加入调用的返回结果集;- 通过
ExtensionLoader::getActivateExtension
方法进行调用获取满足条件的实现类实例;- 支持的方式有分组 (用的多),指定
value
关键字 以及排除法;
- 支持的方式有分组 (用的多),指定
以下为 @Activate
的定义:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
// 指定分组, 当ExtensionLoader指定的分组中存在时就返回
String[] group() default {};
// 指定关键字, 当ExtensionLoader指定的关键字中存在时就返回
String[] value() default {};
// 满足条件返回时,要求在指定key值的子类实例前面(排序)
String[] before() default {};
// 满足条件返回时,要求在指定key值的子类实例后面(排序)
String[] after() default {};
// 满足条件返回时, 进行的排序使用的依据
int order() default 0;
}
最后
以上就是漂亮鞋子为你收集整理的Dubbo SPI使用的全部内容,希望文章能够帮你解决Dubbo SPI使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复