Dubbo-扩展机制

一、基本概述

前言

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() {
    // 从 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;
}

2.从指定路径加载

// 常量
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;
}

接着来看它的具体加载过程:

loadDirectory
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) {
        // 日志打印...
    }
}

loadResource
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) {
        // 日志打印...
    }
}
loadClass
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) {
                    // 抛出异常...
                }
            }
        }
    }
}

3.创建

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;
}

不出意外的, 在生成扩展适配实例时,同样使用了缓存机制。

1.从缓存获取

Object instance = cachedAdaptiveInstance.get();

2.创建

先取得扩展适配类,然后通过反射实例该类,再初始化参数变量。

 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;
}

同样的也是先缓存后创建的套路:

1.从缓存获取

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();

2.创建

创建时会先从二级缓存 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 为例来探究下它从初始化到调用的整个过程:

1.初始化

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

2.调用

在 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 在扩展点的初始化时只会生成一个扩展适配实例,只有到方法调用时才会实例化对应的扩展类

  • Ioc 功能

参照上面的解释,在调用时创建对应的实例,本身已经体现了控制反转。

  • Aop 功能

Dubbo 扩展适配实例在执行方法的同时,先加载它的装饰类,通过装饰类的包装达到的环绕特性

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值