Dubbo 框架设计采用了[微内核+插件]的方式,以此来保证框架整体的灵活性,在提升可定制性的同时,避免了自身的臃肿。通过将原本与内核集成在一起的组件分离出来,只提供了特定的接入接口,组件可以独立的发展、更改而不会对现有系统造成改动。
Dubbo 的扩展机制基于 Java 的 SPI,但又不同于它。这里由于篇幅缘故,不再介绍 JDK 的 SPI 机制。它具有以下特点:
再来对几个概念进行解释:
简单来说即接口,准确一点来说是拥有 @SPI 注解的接口,透过扩展点,可以加载各种不同的扩展类。
不同扩展点拥有各自的扩展加载器,在 Dubbo 中, ExtensionLoader 表示一个扩展加载器。
这里我们可以把扩展适配器当成是接口的工厂类,在生成扩展时,它可以采取对应的策略生成不同的扩展实例。
[扩展适配类]其实就是[扩展适配实例]的 Class 类型。在 Dubbo 中,可以被申明为扩展适配类的类具有以下两种特点,满足其一即可:
如上所言,扩展类是扩展的实例的 Class 类型,它代表着最终我们要生成的实例。
扩展装饰类,其实是对扩展类的代理,通过这种装饰器模式,以此来实现对扩展类的 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 的大致情况,下面再来探究下它的具体实现过程。到目前为止,我们大致有了以下概念:
在 Dubbo 中,整个扩展机制的核心即 ExtensionLoader 类。该类包含了 Dubbo 扩展机制的具体实现。在 Dubbo 中,要完成一次方法调用,大致需要经过以下步骤:
接着来看具体的步骤:
生成扩展加载器,即通过扩展点获取创建指定的 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;
}
扩展器的生成过分为了两个部分:校验、创建
接着来看具体的生成过程:
// EXTENSION_LOADERS 本质是一个 ConcurrentMap
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS =
new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
// 变量
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();
}
在获取扩展适配类时,同样采用了缓存机制:
// 这里存储扩展适配类的缓存本质一个 Holder ,它存储了一组 ,且它是非线程安全的
private final Holder<Map<String, Class<?>>> cachedClasses =
new Holder<Map<String, Class<?>>>();
private Map<String, Class<?>> getExtensionClasses() {
// 从 cache 获取
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
// 因为 Hodler 本身是线程不安全的,所以在操作的它的过程中,采用了锁机制来保证执行顺序
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加载
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// 常量
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/";
// 变量
private String cachedDefaultName;
private Map<String, Class<?>> loadExtensionClasses() {
// 取得该类 SPI 注解的 value ,表示默认要生成的扩展实例
// 该变量会在拼凑扩展适配类的代码时用到
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) {
// 抛出异常...
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
// 从指定目录加载该扩展点的所有扩展类实现,即获取接口的所有实现类
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
接着来看它的具体加载过程:
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
// 通过类加载获取资源的所有路径
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
// 加载资源文件
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
// 日志打印...
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL) {
try {
// 通过流读取文件内容
BufferedReader reader =
new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
// 关键-> 加载类
loadClass(extensionClasses, resourceURL,
Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
// 异常处理...
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
// 日志打印...
}
}
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL,
Class<?> clazz, String name) throws NoSuchMethodException {
// 校验
if (!type.isAssignableFrom(clazz)) {
// 抛出异常...
}
// 判断是否存在扩展适配类,存在则直接返回
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
// 抛出异常...
}
}
// 添加扩展包装类集合,通过包装类可以实现 AOP 功能
else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
}
// 添加普通扩展类集合
else {
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length()
- type.getSimpleName().length()).toLowerCase();
} else {
// 抛出异常...
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
// 添加 @Activate 注解类集合
cachedActivates.put(names[0], activate);
}
for (String n : names) {
// 添加 class-name 集合
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
// 添加扩展类集合
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
// 抛出异常...
}
}
}
}
}
private Class<?> createAdaptiveExtensionClass() {
// 动态拼凑出扩展适配类的代码
String code = createAdaptiveExtensionClassCode();
// 获取类加载器
ClassLoader classLoader = findClassLoader();
// 获取编译器
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.
getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).
getAdaptiveExtension();
// 生成 class
return compiler.compile(code, classLoader);
}
private String createAdaptiveExtensionClassCode() {
StringBuilder codeBuilder = new StringBuilder();
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// 关键-> 若扩展点的方法都不存在 @Adaptive 注解,则无法生成扩展适配实例
if (!hasAdaptiveAnnotation){
// 抛出异常...
}
// ...省略部分代码
// 对应上面提到的,将作为扩展点的默认实现
String defaultExtName = cachedDefaultName;
// ...省略部分代码
}
调用如下:
ExtensionLoader.getExtensionLoader(XXX.class).getAdaptiveExtension()
该过程在 ExtensionLoader 的 getAdaptiveExtension 中实现。内容如下:
// 上面介绍过,Holder 是线程不安全的
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
public T getAdaptiveExtension() {
// 1.先从缓存里获取
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 加锁保证操作顺序
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 2.直接创建
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
// 抛出异常...
}
}
} else {
// 抛出异常...
}
}
return (T) instance;
}
不出意外的, 在生成扩展适配实例时,同样使用了缓存机制。
Object instance = cachedAdaptiveInstance.get();
先取得扩展适配类,然后通过反射实例该类,再初始化参数变量。
private T createAdaptiveExtension() {
try {
// 利用反射实例化类得到扩展适配实例,并初始化变量完成扩展
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
// 抛出异常...
}
}
初始化变量过程如下:
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
// 过滤筛选出该类中修饰符为 public 的 setter 方法
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
// 获取[变量名称],即 setterName -> 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) {
method.invoke(instance, object);
}
} catch (Exception e) {
// 日志打印...
}
}
}
}
} catch (Exception e) {
// 日志打印...
}
return instance;
}
该过程发生在方法调用过程中,准确来说是发生在[扩展适配实例]的方法调用过程。以 Protocol$Adaptive 为例,在该类调用暴露服务的方法时,具有以下步骤:
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null){
// 抛出异常...
}
if (arg0.getUrl() == null){
// 抛出异常...
com.alibaba.dubbo.common.URL url = arg0.getUrl();
}
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null){
// 抛出异常...
}
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 T getExtension(String name) {
if (name == null || name.length() == 0) {
// 抛出异常...
}
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从缓存获取
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
// 创建实例
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
同样的也是先缓存后创建的套路:
private final ConcurrentMap<String, Holder<Object>> cachedInstances =
new ConcurrentHashMap<String, Holder<Object>>();
// getExtension
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
创建时会先从二级缓存 EXTENSION_INSTANCES 获取,如果没有才真正创建
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES =
new ConcurrentHashMap<Class<?>, Object>();
private T createExtension(String name) {
// 获取扩展类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 先二级缓存获取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 为空,则利用反射实例化该类
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 初始化变量
injectExtension(instance);
// 关键 -> 将实例存放进装饰类,以此来实现 Aop 功能
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension(
(T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
// 抛出异常...
}
}
// 获取扩展类,同生成扩展适配类同样的套路
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
以 Protocol 为例来探究下它从初始化到调用的整个过程:
Protocol 的初始化发生在 Srping 容器加载 Bean 的过程中,当 Dubbo 的 XML 解析器 (DubboBeanDefinitionParser)在解析 xml 时:
<dubbo:service interface="com.oxf.api.HelloRmservice" ref="HelloRmservice"/>
会将其转化成 ServiceBean 实例,而该类由继承自 ServiceConfig 类,Protocol 的初始化就是从这里开始
private static final Protocol protocol =
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
根据上面的分析可知道最终会 Dubbo 会生成一个扩展适配实例:Protocol$Adaptive 。
在 ServiceBean 实例注入到 Spring 容器后,因为它实现了 ApplicationContextAware 接口,所以会它发出一个通知事件
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
// 日志打印...
export();
}
}
这里会将已经初始化的服务暴露出去,(此处略过过程),直接来看本地暴露的代码
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
// 省略部分代码...
// protocol 即 Protocol$Adaptive
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
}
}
在 Protocol$Adaptive 调用 export 方法时,会依次先生成如下实例
// Protocol$Adaptive ->
// QosProtocolWrapper->
// ProtocolFilterWrapper->
// ProtocolListenerWrapper->
// InjvmProtocol
先经过装饰类,最终落到扩展实例的执行。
再来回顾上介绍过关于 Dubbo 扩展模式的几个优点:
Dubbo 在扩展点的初始化时只会生成一个扩展适配实例,只有到方法调用时才会实例化对应的扩展类
参照上面的解释,在调用时创建对应的实例,本身已经体现了控制反转。
Dubbo 扩展适配实例在执行方法的同时,先加载它的装饰类,通过装饰类的包装达到的环绕特性