Dubbo源码解析-SPI.Dubbo可扩展机制原理

1.背景,改进


     SPI的全名为Service Provider Interface,面向对象的设计里面,模块之间推荐基于接口编程,而不是对实现类进行硬编码,这样做也是为了模块设计的可拔插原则。为了在模块装配的时候不在程序里指明是哪个实现,就需要一种服务发现的机制,jdk的spi就是为某个接口寻找服务实现

   了解Dubbo/阅读过官网文档的同学,可以看到Dubbo内部存在着大量的扩展,如Protocol:DubboProtocol,InJvmProtocol; Cluster:FailoverCluster,FailFastCluser;LoadBalance:RandomLoadBalance,LeastActiveLoadBalance等等;
    Dubbo的SPI扩展机制主要在JDK的扩展机制上进行了改进:主要原因如下

  • 需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。
  • 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
  • 扩展如果依赖其他的扩展,做不到自动注入和装配
  • 不提供类似于Spring的IOC和AOP功能
  • 扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持

2.Dubbo—SPI常见术语(摘自官网)

    2.1 扩展点(Extension Point)

           是一个Java的接口。

    2.2 扩展(Extension)

           扩展点的实现类。

    2.3 扩展实例(Extension Instance)

           扩展点实现类的实例。

    2.4 扩展自适应实例(Extension Adaptive Instance)

     第一次接触这个概念时,可能不太好理解(我第一次也是这样的...)。如果称它为扩展代理类,可能更好理解些。扩展的自适应 实例其实就是一个Extension的代理,它实现了扩展点接口。在调用扩展点的接口方法时,会根据实际的参数来决定要使用哪个扩 展。比如一个IRepository的扩展点,有一个save方法。有两个实现MysqlRepository和MongoRepository。IRepository的自适应实例在调用接口方法的时候,会根据save方法中的参数,来决定要调用哪个IRepository的实现。如果方法参数中有repository=mysql,那么就调用MysqlRepository的save方法。如果repository=mongo,就调用MongoRepository的save方法。和面向对象的延迟绑定很类似。为什么Dubbo会引入扩展自适应实例的概念呢?

  • Dubbo中的配置有两种,一种是固定的系统级别的配置,在Dubbo启动之后就不会再改了。还有一种是运行时的配置,可能对于每一次的RPC,这些配置都不同。比如在xml文件中配置了超时时间是10秒钟,这个配置在Dubbo启动之后,就不会改变了。但针对某一次的RPC调用,可以设置它的超时时间是30秒钟,以覆盖系统级别的配置。对于Dubbo而言,每一次的RPC调用的参数都是未知的。只有在运行时,根据这些参数才能做出正确的决定。
  • 很多时候,我们的类都是一个单例的,比如Spring的bean,在Spring bean都实例化时,如果它依赖某个扩展点,但是在bean实例化时,是不知道究竟该使用哪个具体的扩展实现的。这时候就需要一个代理模式了,它实现了扩展点接口,方法内部可以根据运行时参数,动态的选择合适的扩展实现。而这个代理就是自适应实例。 自适应扩展实例在Dubbo中的使用非常广泛,Dubbo中,每一个扩展都会有一个自适应类,如果我们没有提供,Dubbo会使用字节码工具为我们自动生成一个。所以我们基本感觉不到自适应类的存在。后面会有例子说明自适应类是怎么工作的。

    2.5 @SPI

      @SPI注解作用于扩展点的接口上,表明该接口是一个扩展点。可以被Dubbo的ExtentionLoader加载。如果没有此ExtensionLoader调用会异常。

   2.6 @Adaptive

    @Adaptive注解用在扩展接口的方法上。表示该方法是一个自适应方法。Dubbo在为扩展点生成自适应实例时,如果方法有@Adaptive注解,会为该方法生成对应的代码。方法内部会根据方法的参数,来决定使用哪个扩展。 @Adaptive注解用在类上代表实现一个装饰类,类似于设计模式中的装饰模式,它主要作用是返回指定类,目前在整个系统中AdaptiveCompiler、AdaptiveExtensionFactory这两个类拥有该注解。

   2.7 ExtentionLoader

类似于Java SPI的ServiceLoader,负责扩展的加载和生命周期维护。

    2.8 扩展别名

和Java SPI不同,Dubbo中的扩展都有一个别名,用于在应用中引用它们。比如

random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance

其中的random,roundrobin就是对应扩展的别名。这样我们在配置文件中使用random或roundrobin就可以了。

2.9 一些路径

和Java SPI从/META-INF/services目录加载扩展配置类似,Dubbo也会从以下路径去加载扩展配置文件:

  • META-INF/dubbo/internal
  • META-INF/dubbo
  • META-INF/services

3.Dubbo-SPI机制测试

    项测项目结构,定义Roboot接口,定义void  sayHello()方法,注意Roboot接口定义SPI注解,SPI(key)表示默认为value(对应键值对中的key);

两个Roboot实现类,Bumbleee,OptimusPrime;
Bumbleee类:

package com.didispace.spi.impl;

import com.didispace.spi.Robot;

/**
 * @Author: SoftWareKang
 * @Name:SpringCloud-Learning
 * @Date: 2020/5/24 17:33
 */
public class Bumblebee implements Robot {

    public static final String NAME = "bumblebee";

    @Override
    public void sayHello() {
        System.out.println("I'm Bumblebee");
    }
}

OptimusPrime类:

package com.didispace.spi.impl;

import com.didispace.spi.Robot;

/**
 * @Author: SoftWareKang
 * @Name:SpringCloud-Learning
 * @Date: 2020/5/24 17:33
 */
public class OptimusPrime implements Robot {
    public static final String NAME = "optimusPrime";

    @Override
    public void sayHello() {
        System.out.println("I'm OptimusPrime");
    }
}

key-value文件:META-INF/dubbo/com.didispace.spi.Robot,文件内容如下

optimusPrime = com.didispace.spi.impl.OptimusPrime
bumblebee = com.didispace.spi.impl.Bumblebee

测试类:

@Test
	public void spiTest() {
		ExtensionLoader<Robot> extensionLoader =
				ExtensionLoader.getExtensionLoader(Robot.class);
		Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
		optimusPrime.sayHello();
		Robot bumblebee = extensionLoader.getExtension("bumblebee");
		bumblebee.sayHello();
	}

测试结果:控制台会打印如下结果;
I'm OptimusPrime
I'm Bumblebee
 

4.基本用法了解,探测源码

ExtensionLoader 是最核心的类,负责扩展点的加载和生命周期管理。 ExtensionLoader 的方法比较多,比较常用的方法有:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)
public T getExtension(String name)
public T getAdaptiveExtension()

getExtensionLoader方法 这是一个静态工厂方法,入参是一个可扩展的接口,返回一个该接口的ExtensionLoader实体类。通过这个实体类,可以根据name获得具体的扩展,也可以获得一个自适应扩展。

 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        // type如果为空返回参数异常;
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
         // 如果type不是接口类型,返回参数异常
        } else if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        //  如果没有@SPI注解,返回参数异常
        } else if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        } else {
        // 获取ExtensionLoader实例
            ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            if (loader == null) {
                // 放入缓存中
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                // loader赋值
                loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            }
            
            return loader;
        }
    }


  private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
// 根据name即key获取对用接口实现类的实例
public T getExtension(String name) {
        // 判断参数:如果name不为null或不为"",否则返回参数异常
        if (name != null && name.length() != 0) {
            if ("true".equals(name)) {
                return this.getDefaultExtension();
            } else {
                // 缓存中获取holdler:可以理解为对一个对象持有的工具类
                Holder<Object> holder = (Holder)this.cachedInstances.get(name);
                if (holder == null) {
                    // 没有则创建holder
                    this.cachedInstances.putIfAbsent(name, new Holder());
                    holder = (Holder)this.cachedInstances.get(name);
                }
                // 获取该对象实例的值
                Object instance = holder.get();
                // 标准的双重判断
                if (instance == null) {
                    synchronized(holder) {
                        instance = holder.get();
                        if (instance == null) {
                            // 核心方法,创建实例
                            instance = this.createExtension(name);
                            holder.set(instance);
                        }
                    }
                }

                return instance;
            }
        } else {
            throw new IllegalArgumentException("Extension name == null");
        }
    }
   // holder类
public class Holder<T> {
    private volatile T value;

    public Holder() {
    }

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return this.value;
    }
}

核心方法private T createExtension(String name)方法

 private T createExtension(String name) {
       // 根据扩展节点名称获取对应class
        Class<?> clazz = (Class)this.getExtensionClasses().get(name);
        if (clazz == null) {
            throw this.findException(name);
        } else {
            try {
                // 缓存中获取实例
                T instance = EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    // 如果为空,调用newInstance()方法生成实例
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = EXTENSION_INSTANCES.get(clazz);
                }
                // IOC注入属性
                this.injectExtension(instance);
                // 如果有wrapper,添加wrapper
                Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                Class wrapperClass;
                if (wrapperClasses != null && wrapperClasses.size() > 0) {
                    for(Iterator i$ = wrapperClasses.iterator(); i$.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                        wrapperClass = (Class)i$.next();
                    }
                }

                return instance;
            } catch (Throwable var7) {
                throw new IllegalStateException("Extension instance(name: " + name + ", class: " + this.type + ")  could not be instantiated: " + var7.getMessage(), var7);
            }
        }
    }

核心: private Map<String, Class<?>> getExtensionClasses()方法

 private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = (Map)this.cachedClasses.get();
        if (classes == null) {
            Holder var2 = this.cachedClasses;
            synchronized(this.cachedClasses) {
                classes = (Map)this.cachedClasses.get();
                if (classes == null) {
                    // 根据配置,加载classes
                    classes = this.loadExtensionClasses();
                    this.cachedClasses.set(classes);
                }
            }
        }

        return classes;
    }

核心:private Map<String, Class<?>> loadExtensionClasses()方法,private void loadFile(Map<String, Class<?>> extensionClasses, String dir)方法

private Map<String, Class<?>> loadExtensionClasses() {
        // 获取SPI默认值
        SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                // 默认值只能唯一
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + this.type.getName() + ": " + Arrays.toString(names));
                }

                if (names.length == 1) {
                    // 缓存
                    this.cachedDefaultName = names[0];
                }
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap();
        // 加载三个目录下的配置
        this.loadFile(extensionClasses, "META-INF/dubbo/internal/");
        this.loadFile(extensionClasses, "META-INF/dubbo/");
        this.loadFile(extensionClasses, "META-INF/services/");
        return extensionClasses;
    }
   
 
    // 加载的核心方法,extensionClasses缓存class
    private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        // 拼接fileName
        String fileName = dir + this.type.getName();

        try {
            // 加载器
            ClassLoader classLoader = findClassLoader();
            Enumeration urls;
             // 根据文件名获取所有URL
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            
            if (urls != null) {
                label269:
                while(urls.hasMoreElements()) {
                    java.net.URL url = (java.net.URL)urls.nextElement();

                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));

                        try {
                            String line = null;

                            while(true) {
                                do {
                                    if ((line = reader.readLine()) == null) {
                                        continue label269;
                                    }
                                    // 定位#字符,截取,因为是注释
                                    int ci = line.indexOf(35);
                                    if (ci >= 0) {
                                        line = line.substring(0, ci);
                                    }
                                    // 去除空格
                                    line = line.trim();
                                } while(line.length() <= 0);

                                try {
                                    String name = null;
                                    int i = line.indexOf(61);
                                    if (i > 0) {
                                        // 获取key-value
                                        name = line.substring(0, i).trim();
                                        line = line.substring(i + 1).trim();
                                    }

                                    if (line.length() > 0) {
                                        // class.forname获取class
                                        Class<?> clazz = Class.forName(line, true, classLoader);
                                        if (!this.type.isAssignableFrom(clazz)) {
                                            throw new IllegalStateException("Error when load extension class(interface: " + this.type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
                                        }

                                        if (clazz.isAnnotationPresent(Adaptive.class)) {
                                            if (this.cachedAdaptiveClass == null) {
                                                this.cachedAdaptiveClass = clazz;
                                            } else if (!this.cachedAdaptiveClass.equals(clazz)) {
                                                throw new IllegalStateException("More than 1 adaptive class found: " + this.cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
                                            }
                                        } else {
                                            try {
                                              // // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
                                                clazz.getConstructor(this.type);
                                                Set<Class<?>> wrappers = this.cachedWrapperClasses;
                                                if (wrappers == null) {
                                                    this.cachedWrapperClasses = new ConcurrentHashSet();
                                                    wrappers = this.cachedWrapperClasses;
                                                }
                                                // 缓存clazz
                                                wrappers.add(clazz);
                                            } catch (NoSuchMethodException var27) {
                                                clazz.getConstructor();
                                                if (name == null || name.length() == 0) {
                                                    name = this.findAnnotationName(clazz);
                                                    if (name == null || name.length() == 0) {
                                                        if (clazz.getSimpleName().length() <= this.type.getSimpleName().length() || !clazz.getSimpleName().endsWith(this.type.getSimpleName())) {
                                                            throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                        }

                                                        name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - this.type.getSimpleName().length()).toLowerCase();
                                                    }
                                                }

                                                String[] names = NAME_SEPARATOR.split(name);
                                                if (names != null && names.length > 0) {
                                                    Activate activate = (Activate)clazz.getAnnotation(Activate.class);
                                                    if (activate != null) {
                                                        this.cachedActivates.put(names[0], activate);
                                                    }

                                                    String[] arr$ = names;
                                                    int len$ = names.length;

                                                    for(int i$ = 0; i$ < len$; ++i$) {
                                                        String n = arr$[i$];
                                                        if (!this.cachedNames.containsKey(clazz)) {
                                                            this.cachedNames.put(clazz, n);
                                                        }

                                                        Class<?> c = (Class)extensionClasses.get(n);
                                                        if (c == null) {
                                                            extensionClasses.put(n, clazz);
                                                        } else if (c != clazz) {
                                                            throw new IllegalStateException("Duplicate extension " + this.type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } catch (Throwable var28) {
                                    IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + this.type + ", class line: " + line + ") in " + url + ", cause: " + var28.getMessage(), var28);
                                    this.exceptions.put(line, e);
                                }
                            }
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable var30) {
                        logger.error("Exception when load extension class(interface: " + this.type + ", class file: " + url + ") in " + url, var30);
                    }
                }
            }
        } catch (Throwable var31) {
            logger.error("Exception when load extension class(interface: " + this.type + ", description file: " + fileName + ").", var31);
        }

    }

loadExtensionClasses()的基本流程:

1.SPI注解解析
2.调用 loadDirectory 方法加载指定文件夹配置文件。
3.loadResource 方法用于读取和解析配置文件,并通过反射加载类;
4.缓存相关信息
 

5.Dubbo-IOC源码
 

private T createExtension(String name)方法在创建完实例后,执行// IOC注入属性 this.injectExtension(instance);
我们进入源码分析

 private T injectExtension(T instance) {
        try {
            if (this.objectFactory != null) {
                // 获取所有方法
                Method[] arr$ = instance.getClass().getMethods();
                int len$ = arr$.length;
 
                for(int i$ = 0; i$ < len$; ++i$) {
                   // 如果方法为set方法,且级别为public
                    Method method = arr$[i$];
                    if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
                        Class pt = method.getParameterTypes()[0];
                        
                        try {
                            // 获取属性名
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                           // 通过工厂生成对象
                            Object object = this.objectFactory.getExtension(pt, property);
                           // 通过反射赋值
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception var9) {
                            logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
                        }
                    }
                }
            }
        } catch (Exception var10) {
            logger.error(var10.getMessage(), var10);
        }

        return instance;
    }

在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。
Dubbo-IOC目前主要是set方法,很简单;

6.总结

  1. 生成接口类的ExtensionLoader对象:
    1. 参数判空,接口类型校验,@SPI注解校验
    2. 生成ExtensionLoader,objectFactory;   
  2.  根据ExtensionLoader.getExtensionName(name)获取扩展节点实例;
    1. 查询缓存的handler,没有则生成handler;查询handler的值,为空则createExtension(name)创建对应实例;
    2. 根据配置,底层根据class.forName生成class对象实例,返回class对象
    3. 调用class.newInstance创建实例,初始化,IOC赋值等;
  3. 根据生成的实例,进行面向接口编程;

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章