Dubbo SPI
在Dubbo的源码中,可以看到很多地方使用了ExtensionLoader来获取具体的扩展类,以Protocol为例,Protocol是一个接口,它可以有多种协议,那么具体选择哪一种协议呢,就是通过ExtensionLoader的getExtensionLoader方法获取Protocol对应的ExtensionLoader对象,然后调用其getAdaptiveExtension方法获取具体的实现类的,它可以根据配置选择不同的协议:
1 | private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); |
可以看到Protocol使用到了@SPI注解,默认使用dubbo协议:
1 | "dubbo") ( |
在META-INF/dubbo下面可以看到dubbo对应的类为org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol:
1 | dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol |
因为META-INF下dubbo对应的扩展类为DubboProtocol,所以通过ExtensionLoader获取Protocol具体的实现类时,如果没有指定协议,将默认使用DubboProtocol。
Dubbo还使用了自适应扩展机制,也就是@Adaptive,它可以加载类上,也可以加在某个方法上,加在某个方法中时,会动态生成字节码创建自适应对象Protocol$Adaptive,Protocol$Adaptive中会根据url中的协议选择不同的实现类。接下来就进入ExtensionLoader中看一下SPI机制的实现原理。
ExtensionLoader
getExtensionLoader方法
EXTENSION_LOADERS:缓存每个class对象对应的ExtensionLoader,dubbo会为每个class都创建一个ExtensionLoader对象。
getExtensionLoader方法用来根据class对象获取对应的ExtensionLoader,就是从EXTENSION_LOADERS获取的,如果为空,会创建一个ExtensionLoader对象并放入EXTENSION_LOADERS中:
1 | public class ExtensionLoader<T> { |
ExtensionLoader中还有一个getExtension方法,它可以根据名称来获取对应的实现类,比如自定义了一个Protocol的实现类MyDubboProtocol:
1 | public class MyDubboProtocol implements Protocol { |
在META-INF/dubbo下面配置一个自定义的协议,key为mydubbo,value为MyDubboProtocol全限定类名:
1 | mydubbo=org.apache.dubbo.rpc.protocol.dubbo.MyDubboProtocol |
接下来通过ExtensionLoader的getExtension方法中传入mydubbo就可以获取到MyDubboProtocol:
1 | // 此时获取的Protocol类型就是MyDubboProtocol |
getExtension方法
getExtension方法,会根据传入的名称获取对应的实例化对象,如果对象为空,会调用createExtension方法创建扩展对象:
1 | // 根据名称获取对象 |
createExtension
createExtension方法用于创建扩展对象:
- 它会调用getExtensionClasses获取所有的class
- 根据传入的名称name获取class对象
- 根据第2步获取的class对象,从EXTENSION_INSTANCES(缓存了每个class对应的实例化对象)中获取class对应的实例化对象,如果为空,通过newInstance实例化一个对象
1 | // 缓存每个class对象对应的实例化对象 |
getExtensionClasses
getExtensionClasses用于获取所有的扩展类的class信息,如果为空调用loadExtensionClasses方法加载所有的class信息,并放入一个map中,将map封装为一个Holer对象,也就是cachedClasse:
1 |
|
比如dubbo协议,key为dubbo,value为org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol,在getExtensionClasses方法中就会将这些信息放入到cachedClasses中:
1 | dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol |
loadExtensionClasses
loadExtensionClasses加载所有的扩展类的class信息,它会遍历所有的加载策略,从对应的目录下加载class信息:
1 | // 加载class |
LoadingStrategy
1 | // loadLoadingStrategies获取所有的加载策略 |
LoadingStrategy是一个接口,它有四个实现类:
以DubboLoadingStrategy为例,可以看到directory的值为”META-INF/dubbo/“,所以loadExtensionClasses中会加载META-INF/dubbo/下的所有配置:
1 | public class DubboLoadingStrategy implements LoadingStrategy { |
Adaptive自适应扩展
在最开始,获取Protocol的时候调用的是getAdaptiveExtension方法,并且Protocol的export和refer上添加了@Adaptive注解:
1 | private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); |
getAdaptiveExtension
getAdaptiveExtension方法用来获取自适应扩展对象,如果为空,将会调用createAdaptiveExtension创建扩展对象:
1 | private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); |
createAdaptiveExtension
createAdaptiveExtension用于创建自适应扩展对象,它会通过AdaptiveClassCodeGenerator动态生成字节码,创建代理对象XXX$Adaptive:
1 | private T createAdaptiveExtension() { |
以Protocol为例,会动态生成Protocol$Adaptive类,在Protocol$Adaptive中实现了Protocol接口中的方法,以export为例,它根据url中的协议获取扩展名,再根据名称调用上面提到的getExtension方法获取具体的实现类,所以在这里才会生成DubboProtocol,之后调用DubboProtocol的export方法:
1 | public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { |
参考
dubbo版本:2.7.7