Dubbo扩展点加载的功能
Dubbo的扩展点加载机制类似于Java的SPI,我们知道Java的SPI在使用的时候,只能通过遍历来进行实现的查找和实例化,有可能会一次性把所有的实现都实例化,这样会造成有些不使用的扩展实现也会被实例化,这就会造成一定的资源浪费。有关Dubbo的改进,参照文档上的说明:
- JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK标准的ScriptEngine,通过getName();获取脚本类型的名称,但如果RubyScriptEngine因为所依赖的jruby.jar不存在,导致RubyScriptEngine类加载失败,这个失败原因被吃掉了,和ruby对应不起来,当用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因。
- 增加了对扩展点IoC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。
Dubbo扩展点的特征
-
@SPI注解,被此注解标记的接口,就表示是一个可扩展的接口。
-
@Adaptive注解,有两种注解方式:一种是注解在类上,一种是注解在方法上。
- 注解在类上,而且是注解在实现类上,目前dubbo只有AdaptiveCompiler和AdaptiveExtensionFactory类上标注了此注解,这是些特殊的类,ExtensionLoader需要依赖他们工作,所以得使用此方式。
- 注解在方法上,注解在接口的方法上,除了上面两个类之外,所有的都是注解在方法上。ExtensionLoader根据接口定义动态的生成适配器代码,并实例化这个生成的动态类。被Adaptive注解的方法会生成具体的方法实现。
没有注解的方法生成的实现都是抛不支持的操作异常UnsupportedOperationException。被注解的方法在生成的动态类中,会根据url里的参数信息,来决定实际调用哪个扩展。
例如:
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
dubbo实现SPI
在 Dubbo 中,想要拓展拓展点,只需要以下几个步骤
1.创建拓展点实现类 (以LoadBalance为例):
public class MyLoadBalance extends AbstractLoadBalance {
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
return null;
}
}
2.在指定文件夹下创建以拓展点全路径名(org.apache.dubbo.rpc.cluster.LoadBalance)的文件,Dubbo 中有多个目录都可以配置拓展点,这里用在resource/META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance
myLoadBalance=com.demo.loadbalance.MyLoadBalance
3.测试
public class TestLoadBalance {
public static void main(String[] args) {
LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("myLoadBalance");
System.out.println(loadBalance);
}
}
可以发现我们已经可以拿到我们自己的实现类了。
那么他具体是怎么实现的呢?让我们继续往下看 Dubbo SPI扩展点(二)