版本
2.5.7
ExtensionLoader机制
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
- JDK 标准的 SPI 会⼀次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源;
- 如果扩展点加载失败,连扩展点的名称都拿不到了。⽐如:JDK 标准的 ScriptEngine,通过getName()获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine类加载失败,这个失败原因被吃掉了,和 ruby对应不起来,当用户执行ruby 脚本时,会报不支持ruby,而不是真正失败的原因;
- 增加了对扩展点 IoC 和 AOP 的支持,⼀个扩展点可以直接 setter 注⼊其它扩展点。
以上内容摘自官方文档
ExtensionLoader的作用就是加载所有打上了@SPI
注解的接口,并根据配置进行实例化、封装,包括dubbo自己的服务调用、暴露等功能,也是使用这种方式实现的。
加载过程
1、实例化ExtensionLoader
构造方法:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
从代码中可以看出,ExrtensionLoader
与SPI
是一一对应的关系,且每个封装了SPI
的ExrtensionLoader
都保存了一个objectFactory
对象,而objectFactory
指向的都是ExrtensionLoader
的adaptive实现,即AdaptiveExtensionFactory
。
1.1 AdaptiveExtension实例化过程
getAdaptiveExtension
的实例化使用了互斥锁,检查了是否有实例化异常 的缓存后,通过createAdaptiveExtension()
方法实例化对象。
在实例化的过程中,会再次执行getExtensionClasses()
方法检查是否已加载过配置文件。 以ExtensionFactory
举例,因为在dubbo-common
的配置文件中,已经默认配置了ExtensionFactory
的两个实现,且其AdaptiveExtensionFactory
上打了@Adaptive
注解,所以默认情况下会返回缓存中,第一次加载配置文件时初始化的AdaptiveExtensionFactory
类。
但如果缓存中没有,比如Protocol
就没有在实现类类上注解@Adaptive
,也没关系,ExrtensionLoader
会通过createAdaptiveExtensionClass()
方法生成代码,再编译成class字节码。
Protocol接口源码
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
void destroy();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
}
Protocol动态生成的实现类:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
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.export(arg0);
}
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);
}
}
可以看出,打了@Adaotive
注解的方法,都会动态生成根据URL获取信息的代码,再组合成对应的实现类,并存储到ExtensionLoader
的静态变量中,以后就可以通过ExtensionLoader
加载对应的SPI
实现,然后执行业务逻辑。
2、获取ExtensionLoader
名称 | 配置位置 | getExtension方法实现 |
---|---|---|
SpiExtensionFactory | dubbo-common | 从ExtensionLoader 的静态Map中读取对象,若没有,则通过new 关键字创建ExtensionLoader
|
SpringExtensionFactory | dubbo-config/dubbo-config-spring | 从Spring上下文中读取对象 |
ExtensionFactory
也是一种SPI
,所有的SPI
都是通过ExtensionLoader
来封装、加载的,每个被封装的对象中都由ExtensionFactory objectFactory
属性持有一个ExtensionFactory
的Adaptive实现AdaptiveExtensionFactory
,而AdaptiveExtensionFactory
又由List<ExtensionFactory> factories
持有两个对象SpiExtensionFactory
及SpringExtensionFactory
。
他们的对比如下:
名称 | 配置位置 | getExtension方法实现 |
---|---|---|
SpiExtensionFactory | dubbo-common | 从ExtensionLoader 的静态Map中读取对象,若没有,则通过new 关键字创建ExtensionLoader
|
SpringExtensionFactory | dubbo-config/dubbo-config-spring | 从Spring上下文中读取对象 |
当执行AdaptiveExtensionFactory
的getExtension()
方法时,它会轮询二者之中有没有要返回的对象,优先SPI的方式。
总结
通过ExtensionLoader
机制,dubbo封装了所有SPI实现,并能够使用@Adaptive
注解指定默认的实现,或者注解在方法上,动态将不同实现类的A\B\C方法组合成新的实现类,在灵活、可配、按需加载等方面做的非常出色。