Dubbo源码分析2之SPI自适应扩展Adaptive原理

目录

  • 原理概述
  • 为什么设计@Adaptive注解
  • dubbo源码中哪些地方什么时候使用到了,具体如何体现
  • @Adaptive扩展加载过程源码分析
  • 总结

原理概述

首先看以下官网的关于自适应扩展的描述:

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。

好像还是有点抽象,首先回顾下dubbo的SPI是什么:spi是一种扩展机制,目的就是为了获取一个对象。通过ExtensionLoader.getExtension(String name)获取,通过ExtensionLoader.getAdaptiveExtension()获取扩展类,这个扩展类的自适应具体时如何适应的呢,继续往下看

看下@Adaptive注解的源码定义,可以看出这个注解可以用在方法上和类上

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

为什么设计@Adaptive注解

设计Adaptive注解是为了区分人工编码的和动态生成的类

注解在类上时,表示扩展的逻辑有人工编码完成,不需要生成动态代理类(对应的java文件已经存在项目源码中);在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory;AdaptiveCompiler 只有两个实现类JavassistCompiler和JdkCompiler;AdaptiveExtensionFactory只有两个实现类,SpiExtensionFactory和SpringExtensionFactory.

注解在接口方法上,表示表示扩展的逻辑有框架自动生成(对应的java文件在启动后按需要动态拼接字符串生成,编译加载);例如Protocol接口,实现类有DubboProtocol,InjvmProtocol, HttpProtcol 等等

后面会以AdaptiveCompiler和Protocol为例来分析扩展的加载过程

dubbo源码中哪些地方什么时候使用到了,具体如何体现

下面从最基本的xml配置文件开始分析:


从spring.handlers得知解析DubboNamespaceHandler

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }
}

从 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));得知解析service标
签对应的时ServiceBean对象,ServiceBean继承了ServiceConfig

public class ServiceConfig extends AbstractServiceConfig {

    private static final long serialVersionUID = 3033787999037024738L;

    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

    private static final Map RANDOM_PORT_MAP = new HashMap();
    private final List urls = new ArrayList();
    private final List> exporters = new ArrayList>();

ServiceConfig中有两个静态变量初始化
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
这里就使用到了,接下来分析Protocol的自适应扩展加载过程

@Adaptive扩展加载过程源码分析

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

把断点打在这一行上,首先进入getAdaptiveExtension()方法
代码块1.1

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

先从缓存中查找,没有就创建,进入createAdaptiveExtension()
代码块2.1

private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
        }
    }

injectExtension()时dubbo的IOC实现,上一篇文章已经说过;
接下来看 getAdaptiveExtensionClass()方法:
代码块3.1

private Class getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

getExtensionClasses();这个方法很重要,主要时读取MET-INF/dubbo/internal目录下的配置获取扩展的别名和全类名,并同过判断是否类上有@Adaptive注解,并加入缓存变量cachedAdaptiveClass 中,部分代码如下,注意:这个缓存中放的就是前面说的在类上加@Adaptive注解的类
代码块4.1

                                            if (clazz.isAnnotationPresent(Adaptive.class)) {
                                                if (cachedAdaptiveClass == null) {
                                                    cachedAdaptiveClass = clazz;
                                                } else if (!cachedAdaptiveClass.equals(clazz)) {
                                                    throw new IllegalStateException("More than 1 adaptive class found: "
                                                            + cachedAdaptiveClass.getClass().getName()
                                                            + ", " + clazz.getClass().getName());
                                                }
                                            } else {

返回getAdaptiveExtensionClass()方法,此cachedAdaptiveClass为空,所以会执行createAdaptiveExtensionClass()方法,如下:
代码块4.2

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();
        return compiler.compile(code, classLoader);
    }

一行一行分析:
String code = createAdaptiveExtensionClassCode();
这行代码就是通过拼接字符串生产一个Protocol$Adaptice对象,这里就是前面说注解在方法上的情况,自适应扩展就体现在这个对象中,代码如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative 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.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);
    }

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

真正的自适应就体现在这里了,按需加载某一个实现,extName ,如果url中没有传值就使用默认值dubbo,如果传递了就是传的值。

回到代码块4.2这一行,

com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

发现这里又调用getAdaptiveExtension()方法了,循环调用?这里就是上面说的@Adaptive注解加载类上的情况,这个时候就不需要去生成动态类了

private Class getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

当再次调用到这个方法时cachedAdaptiveClass不为空,所以直接返回退出循环了。

注意:每次加载不同的扩展都是使用新的ExtensionLoader实例

总结

本文讲了什么时自适应扩展机制,以及@Adaptive设计的目的,以及注解在方法和类上的区别,并分析了源码如何实现。
到这里自适应@Adaptive原理也讲完了,如果要看源码dubbo SPI和Adaptive原理必须要了解,源码中随处可见ExtensionLoader的使用。

你可能感兴趣的:(Dubbo源码分析2之SPI自适应扩展Adaptive原理)