一、基本概述
前言
Dubbo 框架设计采用了[微内核+插件]的方式,以此来保证框架整体的灵活性,在提升可定制性的同时,避免了自身的臃肿。通过将原本与内核集成在一起的组件分离出来,只提供了特定的接入接口,组件可以独立的发展、更改而不会对现有系统造成改动。
Dubbo 的扩展机制基于 Java 的 SPI,但又不同于它。这里由于篇幅缘故,不再介绍 JDK 的 SPI 机制。它具有以下特点:
- Dubbo 不会一次性加载(实例化)扩展所有实现,从而避免空间和时间的浪费。
- Dubbo 增加了对扩展点 IOC 和 AOP 的支持。
概念
再来对几个概念进行解释:
1.扩展点
简单来说即接口,准确一点来说是拥有 @SPI 注解的接口,透过扩展点,可以加载各种不同的扩展类。
2.扩展加载器
不同扩展点拥有各自的扩展加载器,在 Dubbo 中, ExtensionLoader 表示一个扩展加载器。
3.扩展适配实例&扩展适配类
这里我们可以把扩展适配器当成是接口的工厂类,在生成扩展时,它可以采取对应的策略生成不同的扩展实例。
[扩展适配类]其实就是[扩展适配实例]的 Class 类型。在 Dubbo 中,可以被申明为扩展适配类的类具有以下两种特点,满足其一即可:
- 实现自扩展点,且扩展点的方法具有 @Adaptive 注解,该方式在运行时动态生成,即随处可见 xxx$Active 类。
- 实现自扩展点,且该类具有 @Adaptive 注解,常见的如 AdaptiveExtensionFactory 等。
4.扩展实例&扩展类
如上所言,扩展类是扩展的实例的 Class 类型,它代表着最终我们要生成的实例。
5.扩展装饰类
扩展装饰类,其实是对扩展类的代理,通过这种装饰器模式,以此来实现对扩展类的 AOP 。
比较
总的来说,我们可以把 Dubbo 的扩展机制,理解成是工厂模式的另类实现。但它比工厂模式来的更为灵活,优雅。
以普通接口的工厂模式为例:
// 接口
public interface Person {
void say();
}
// 实现类
public class Man implements Person {
public void say() {
System.out.println("this is a man");
}
}
public class Woman implements Person {
public void say() {
System.out.println("this is a woman");
}
}
public class NullPerson implements Person {
public void say() {
System.out.println("this is null");
}
}
// 工厂类
public class PersonFactory implements Person {
public static Person create(String sex) {
if ("man".equals(sex)) {
return new Man();
} else if ("woman".equals(sex)) {
return new Woman();
}
return new NullPerson();
}
}
// 调用
PersonFactory.create("man").say();
再来看看换成 Dubbo 会是怎样的实现过程:
// 具体细节暂时不表述,总的来说都是这样的调用方式
ExtensionLoader.getExtensionLoader(Person.class).getAdaptiveExtension().say();
二、实现过程
上面介绍了 Dubbo 的大致情况,下面再来探究下它的具体实现过程。到目前为止,我们大致有了以下概念:
- 扩展点即接口
- 扩展适配类即工厂类
- 扩展类即接口的具体实现类
- 扩展装饰类即实现类的装饰类
- 扩展机制具有 Ioc,Aop 的特性
在 Dubbo 中,整个扩展机制的核心即 ExtensionLoader 类。该类包含了 Dubbo 扩展机制的具体实现。在 Dubbo 中,要完成一次方法调用,大致需要经过以下步骤:
- 生成扩展加载器(getExtensionLoader)
- 生成扩展适配类(getAdaptiveExtensionClass)
- 生成扩展适配实例(getAdaptiveExtension)
- 生成扩展实例(getExtension),该过程发生在执行方法时
接着来看具体的步骤:
生成扩展加载器
生成扩展加载器,即通过扩展点获取创建指定的 ExtensionLoader。上面提到,不同的扩展点都有不同的扩展加载器,虽然具有多个实例,但是它们共享缓存。
调用如下:
ExtensionLoader.getExtensionLoader(XXX.class)
具体步骤如下:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// 1.校验
if (type == null){
// 抛出异常...
}
if (!type.isInterface()) {
// 抛出异常...
}
// 判断是否存在 SPI 注解,即判断该类是否为扩展点
if (!withExtensionAnnotation(type)) {
// 抛出异常...
}
// 2.获取
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
扩展器的生成过分为了两个部分:校验、创建
- 校验:要求被的扩展的类型(即扩展点)不能为空,且必须是被 SPI 注解的接口
- 生成:扩展器的生成使用了缓存机制,先从缓存中获取,若没有再创建新的扩展加载器
接着来看具体的生成过程:
1.从缓存获取
// EXTENSION_LOADERS 本质是一个 ConcurrentMap
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS =
new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
2.新建
// 变量
private final Class<?> type;
private final ExtensionFactory objectFactory;
// 构造器
private ExtensionLoader(Class<?> type) {
this.type = type;
// 先利用扩展机制生成 ExtensionFactory 的扩展适配实例
// 且当 type 为 ExtensionFactory 时,它的扩展器扩展器变量 objectFactory 为 null,防止无限递归
objectFactory = (type == ExtensionFactory.class ? null :
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).
getAdaptiveExtension());
}
生成扩展适配类
生成扩展适配类,发生在生成[扩展适配实例]的过程中。与此同时,它还会获取该扩展的所有实现类,然后按照作用分成三类放进缓存,分别是:
- 扩展适配类
- 扩展类
- 扩展装饰类
具体步骤如下:
private Class<?> getAdaptiveExtensionClass() {
// 1.获取,先从缓存->再从指定目录加载
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 2.创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
在获取扩展适配类时,同样采用了缓存机制:
1.从缓存获取
// 这里存储扩展适配类的缓存本质一个 Holder ,它存储了一组 <k,v>,且它是非线程安全的
private final Holder<Map<String, Class<?>>> cachedClasses =
new Holder<Map<String, Class<?>>>();
private Map<String, Class<?>> getExtensionClasses() {