为什么
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内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复