最近拜读dubbo 的源码,其构架设计是很精彩,基于插件式的构架方式,灵活性可见一般。此外dubbo 框架难得基 服务降级,服务路由,服务发现与注册,服务loadbalance 和 支持多种协议于一身的优秀开源框架,很是值得学习和研究细细读来的。此外整个框架的class 加载是根据统一的URL参数。各个层次结构分明,清晰,可扩展性特别好。读次源码感叹一下!
曾经我再封装一些框架的时候,总是加入一些不必要的依赖,比如封装ribbon,支持多个配置中心的时候,就会发现,会把多个配置中心的依赖都加入进去。开发支持多个校验引擎的时候,心理也是在琢磨着如何更好的切换到不同的校验引擎。Dubbo SPI 提供一种很好的思路。根据统一的URL 配置信息,通过代理类按需加载!
前提:
(1). 理解 jdk SPI
(2). 理解AVAssist 的产生class 字节码,Dubbo 中默认使用AVAssist,主要用来生成代理类
(3). 反射和动态代理(dubbo 的自适应扩张,就是依靠动态代理)
好了,我们来看看dubbo SPI具体的特性。
(1): dubbo SPI 根据 protocol=com.XX.XXX.dubboProtocol, 的方式加载
(2): dubbo SPI 的IOC 特性,dubboProtocol类的setXXX 方法,会通过反射的方式,将相应的class instance, inject 到 dubboProtocol, 通过setXXX的方法。 后面将会有源码。
(3):dubbo SPI 的AOP 特性, ProtocolFilterWrapper 和 ProtocolListenerWrapper这两个类含有protocol 单构造器,放置在loader的私有属性cachedWrapperClasses。ProtocolFilterWrapper在服务的暴露与引用的过程中,根据key 是provider还是consumer来构建服务提供者和消费者调用过滤链。因此具有AOP 的性质。ProtocolListenerWrapper也是在服务暴露与引用的过程中调用listener链。
首先我们需要理解ExtensionLoader, ExtensionFactory这个类。ExtensionFactory 的实现类有 SpiExtensionFactory, SpringExtensionFactory. 默认会加载AdaptiveExtensionFactory (@Adaptive 的注解)实现类。
(1): SpiExtensionFactory:加载有@SPI 的注解的接口实现类。
(2):SpringExtensionFactory: 加载spring context bean 的beans。
默认的实现类AdaptiveExtensionFactory,依赖 List
@SPI
public interface ExtensionFactory {
T getExtension(Class type, String name);
}
接下来就是进入到 ExtensionLoader,SpiExtensionFactory 的 getExtension 服务,会调用ExtensionLoader的 getExtensionLoader方法。然后通过调用 getAdaptiveExtension,加载自适应点。好了,我们进入ExtensionLoader, getExtensionLoader 去看看会发生什么。
但是再这之前,我们看看加载类的配置。配置文件的路径如下:
private static final StringSERVICES_DIRECTORY ="META-INF/services/";
private static final StringDUBBO_DIRECTORY ="META-INF/dubbo/";
private static final StringDUBBO_INTERNAL_DIRECTORY =DUBBO_DIRECTORY +"internal/";
ExtensionLoader 会加载所有classpath 下,该目录的实现类。其implement 的接口都必须有@SPI的注解。
里面的每个类的结构都是 “name” = “com.XXX.XXXX.registryProtocol”的方式来实现的。首先我们来看下ExtensionLoader主要逻辑:
@SuppressWarnings("unchecked")
public static ExtensionLoadergetExtensionLoader(Class type) {
if (type ==null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type +") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" +SPI.class.getSimpleName() +" Annotation!");
}
ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
if (loader ==null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
}
return loader;
}
先从EXTENSION_LOADERS cache 中取出 type 类型的 ExtensionLoader, 比如protocol。 如果没有就会new 一个ExtensionLoader. 我们来看一下new ExtensionLoader的逻辑。
private ExtensionLoader(Class type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ?null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
如果不是ExtensionFactory类型的,就获取一个自适应扩张类。getAdaptiveExtension()。
那么getAdaptiveExtension()是什么逻辑呢?
public T getAdaptiveExtension() {
Object instance =cachedAdaptiveInstance.get();
if (instance ==null) {
if (createAdaptiveInstanceError ==null) {
synchronized (cachedAdaptiveInstance) {
instance =cachedAdaptiveInstance.get();
if (instance ==null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
}catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
}else {
throw new IllegalStateException("fail to create adaptive instance: " +createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
首先也是从cachedAdaptiveInstance 获取, 如果获取不到,通过调用createAdaptiveExtension()。 我们看看createAdaptiveExtension的逻辑。
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " +type +", cause: " + e.getMessage(), e);
}
}
首先是通过getAdaptiveExtensionClass,然后实列化,通过injectExtension 来注入,injectExtension就是上面提到的 IOC 特性,会通过setXXX() 方法,注入对象实列。我们先看
private Map>getExtensionClasses() {
Map> classes =cachedClasses.get();
if (classes ==null) {
synchronized (cachedClasses) {
classes =cachedClasses.get();
if (classes ==null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
首先从cachedClasses 缓存中获取class,如果获取不到,就调用loadExtensionClasses();,加载extension classes.
private Map>loadExtensionClasses() {
final SPI defaultAnnotation =type.getAnnotation(SPI.class);
if (defaultAnnotation !=null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() >0) {
String[] names =NAME_SEPARATOR.split(value);
if (names.length >1) {
throw new IllegalStateException("more than 1 default extension name on extension " +type.getName()
+": " + Arrays.toString(names));
}
if (names.length ==1)cachedDefaultName = names[0];
}
}
Map> extensionClasses =new HashMap>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
首先是设置cachedDefaultName 的默认名字, 然后去load 各个目录下的class, key = value 的形式。
Map> extensionClasses =new HashMap>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
然后进入loadResource 的方法,我们就明白key value 的形式是如何解析的。最后我们调用loadClass 方法,将加载好的class 放入到对应的缓存中。带有adaptive 注解的放入cachedAdaptiveClass, wrapper的class,放入到 cachedWrapperClasses中去。标记有@Active 的类注入到cachedActivates 缓存中去。
将所有的key = value 形式加载完毕之后,我们要看看IOC 是如何作用的。
privateTinjectExtension(T instance){
try {
if (objectFactory != null) {
// 遍历目标类的所有方法 for (Method method : instance.getClass().getMethods()) {
// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
// 获取 setter 方法参数类型 Class> pt = method.getParameterTypes()[0];
try {
// 获取属性名,比如 setName 方法对应属性名 name String property = method.getName().length() > 3 ?
method.getName().substring(3, 4).toLowerCase() +
method.getName().substring(4) : "";
// 从 ObjectFactory 中获取依赖对象 Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 通过反射调用 setter 方法设置依赖 method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method...");
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
通过 Object object = objectFactory.getExtension(pt, property) 和 method.invoke(instance, object) 将adaptive 的类注入的 带有setXXX方法类中。其中objectFactory就是spiExtensionFactory。
那么有没有想过到底是如何使用 自适应扩展点的? 你看,上面通过IOC 和 spiExtensionFactory 将所有的key = value 形式的类都加载好了,存储的形式是map
package com.alibaba.dubbo.rpc; 下的 protocol。 实现类有
那应该使用哪个实现类呢? 这就是自适应扩展点。首先会产生自适应扩展点的代理,然后 在通过URL 的配置信息,传入name, 获取相应的加载类,这就是key value 的原因。
代理类是什么样呢?代理类是通过javassit 产生的, 请看下面:
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.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);
}
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);
}}
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); 就是从 URL 获取 name, 如果获取不到,就默认使用dubbo,通过 getExtension(extName),就可以获取到 加载类: DubboProtocol
那URL 是啥样子呢:dubbo://172.27.238.135:20880/com.mastercard.api.service.DemoService?anyhost=true&application=dubbo-provider&bind.ip=172.27.238.135&bind.port=20880&default.timeout=5000&dubbo=2.6.2&generic=false&interface=com.mastercard.api.service.DemoService&methods=sayHello&pid=71712&revision=1.0.0&side=provider×tamp=1549356336726&version=1.0.0
其实里面的很多内容很精彩,需要自己慢慢品味。后面具体分析下自适应扩展点。