dubbo模块之间都是基于接口调用,dubbo扩展机制就是通过接口寻找具体实现类的过程。
如果一个扩展接口未指定扩展类,则选择默认的key为dubbo,如下图:
源码位置:dubbo-common模块下com.alibaba.dubbo.common.extension。
加在接口上,表明接口为可扩展接口。
通过接口找到实现类过程,中间还要经历适配器,该注解用来定义适配器。
当加在类上的时候,表明该类为适配器类。
当加在方法上的时候,dubbo会动态生成适配器类。
主要用于扩展Filter和Listener。
他自己也是一个扩展接口,分别是SpiExtensionFactory和SpringExtensionFactory代表着两种不同方式去获取实例,而具体选择哪种方式去获取实现类的实例,则在适配器AdaptiveExtensionFactory中制定了规则。
核心类,所有核心逻辑都在该类中实现。接下来开始分析这个类。
存放扩展相关配置文件的路径
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
META-INF/services/:dubbo兼容jdk spi的文件路径
META-INF/dubbo/:提供给用户自己扩展的配置文件的路径
META-INF/dubbo/internal/:dubbo自身内部配置文件路径
比如ExtensionFactory接口的配置文件路径如下图:扩展接口的路径 + 上边定义的路径

扩展加载器集合
key为扩展接口
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
扩展实现类集合
key为扩展实现类,value为扩展对象,例如key为Class
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
以下属性都是cache开头的
都是出于性能和资源的优化,才做的缓存,读取扩展配置后,会先进行缓存,等到真正需要用到某个实现时,再对该实现类的对象进行初始化,然后对该对象也进行缓存。
//缓存的扩展名与扩展类映射,和cachedClasses的key和value对换。
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
//缓存的扩展实现类集合
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
//扩展名与加有@Activate的自动激活类的映射
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//缓存的扩展对象集合,key为扩展名,value为扩展对象
//例如Protocol扩展,key为dubbo,value为DubboProcotol
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
//缓存的自适应( Adaptive )扩展对象,例如例如AdaptiveExtensionFactory类的对象
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//缓存的自适应扩展对象的类,例如AdaptiveExtensionFactory类
private volatile Class<?> cachedAdaptiveClass = null;
//缓存的默认扩展名,就是@SPI中设置的值
private String cachedDefaultName;
//创建cachedAdaptiveInstance异常
private volatile Throwable createAdaptiveInstanceError;
//拓展Wrapper实现类集合
private Set<Class<?>> cachedWrapperClasses;
//拓展名与加载对应拓展类发生的异常的映射
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
这里提到了Wrapper类的概念。那我就解释一下:Wrapper类也实现了扩展接口,但是Wrapper类的用途是ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外,这实现了扩展点自动包装的特性。通俗点说,就是一个接口有很多的实现类,这些实现类会有一些公共的逻辑,如果在每个实现类写一遍这个公共逻辑,那么代码就会重复,所以增加了这个Wrapper类来包装,把公共逻辑写到Wrapper类中,有点类似AOP切面编程思想。
到现在已经生成了一个适配器类(可以把适配器类理解成代理类),接下来调用调用目标方法的时候,适配器类会进行拦截,根据运行时的参数来决定生成哪个实现类,最后在调用生成的实现类的目标方法.
以扩展接口Transporter举个例子,该接口动态生成适配器类,适配器类如下:
Transporter类
@SPI("netty")
public interface Transporter {
/**
* Bind a server.
*
* @param url server url
* @param handler
* @return server
* @throws RemotingException
* @see com.alibaba.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
*/
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
/**
* Connect to a server.
*
* @param url server url
* @param handler
* @return client
* @throws RemotingException
* @see com.alibaba.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
*/
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
Transporter适配器类
public class Transporter$Adaptive implements com.alibaba.dubbo.remoting.Transporter{
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
//URL参数为空则抛出异常。
if (arg0 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
//这里的getParameter方法可以在源码中具体查看
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
//这里我在后面会有详细介绍
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader
(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader
(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
1、所有扩展点都通过传递URL携带配置信息,所以适配器中的方法必须携带URL参数,才能根据URL中的配置来选择对应的扩展实现。
2、@Adaptive注解中有一些key值,比如connect方法的注解中有两个key,分别为“client”和“transporter”,URL会首先去取client对应的value来作为我上述(一)注解@SPI中写到的key值,如果为空,则去取transporter对应的value,如果还是为空,则会根据SPI默认的key,也就是netty去调用扩展的实现类,如果@SPI没有设定默认值,则会抛出IllegalStateException异常。
具体调用链路如下图:删除部分节点,方便阅读

原文:https://www.cnblogs.com/yanhui007/p/12596071.html