Dubbo之SPI源码分析

Posted Java后端笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo之SPI源码分析相关的知识,希望对你有一定的参考价值。

1.Dubbo SPI介绍

Dubbo的扩展点加载机制,用于在程序运行时,通过传入Url里面参数的不同,加载同一接口的不同实现, 同时也支持aop与ioc的功能。

2.使用方式

 
   
   
 
  1. ExtensionLoader.getExtensionLoader(WrappedExt.class).getExtension("XXX");

直接获取具体的扩展点实现,会进行扩展点自动包装(aop)以及扩展点自动装配(ioc)

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

  2. ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl);

获取扩展点的适配类,会在运行时根据url参数调用不同实现类,接口参数要直接或者间接含有Url参数

3.源码分析

Dubbo SPI主要由ExtensionLoader这个类实现,我们的源码讲解也从这个类开始

一些术语

介于扩展点这个名词太过宽泛,在讲解源码的时候,我细化一下 扩展点接口:扩展点所针对的接口,一个接口能有多个扩展点实现 扩展点(名):扩展点实现类对应的别名 扩展点实现:扩展点的具体实现类 扩展点实现实例: 扩展点的具体实现类的实例

一些变量

 
   
   
 
  1. private static final String SERVICES_DIRECTORY = "META-INF/services/";

  2. private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

  3. private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

上述三个常量是,扫描SPI配置文件的路径,文件名为接口的全限定名,文件内容为SPI实现别名=接口实现,多个换行显示,如

 
   
   
 
  1. // 文件名:com.alibaba.dubbo.rpc.Protocol

  2. filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper

  3. listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper

  4. mock=com.alibaba.dubbo.rpc.support.MockProtocol

 
   
   
 
  1. private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

  2. private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

上面有两个静态变量 第一个说明针对每个扩展点接口都会有一个ExtensionLoader对应 第二个是扩展点接口实现类对应实例的缓存

 
   
   
 
  1. private final ExtensionFactory objectFactory;

这个是扩展点工厂,对扩展点实现进行依赖注入的时候使用

 
   
   
 
  1. private volatile Class<?> cachedAdaptiveClass = null;

缓存当前扩展点的适配类

 
   
   
 
  1. private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();

缓存当前扩展点适配类实例

 
   
   
 
  1. private Set<Class<?>> cachedWrapperClasses;

缓存包装类

初始化流程

先看下ExtensionLoader的构造函数

 
   
   
 
  1. private ExtensionLoader(Class<?> type) {

  2.        this.type = type;

  3.        //扩展点工厂不需要设置这个,不然会无限循环

  4.        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

  5.    }

这边会给当前的ExtensionLoader设置扩展点接口类型,以及扩展点工厂(用于依赖注入) 但是这边并没有对配置的扩展点进行加载,出于性能优化,并不是预加载,扩展点的加载在实际使用的时候在加载,比如

 
   
   
 
  1. private Class<?> getAdaptiveExtensionClass() {

  2.        getExtensionClasses();

  3.        if (cachedAdaptiveClass != null) {

  4.            return cachedAdaptiveClass;

  5.        }

  6.        return cachedAdaptiveClass = createAdaptiveExtensionClass();

  7.    }

这个方法用来获取当前扩展点的适配器类,扩展点的加载在getExtensionClasses方法内实现

 
   
   
 
  1. private Map<String, Class<?>> getExtensionClasses() {

  2.        Map<String, Class<?>> classes = cachedClasses.get();

  3.        if (classes == null) {

  4.            synchronized (cachedClasses) {

  5.                classes = cachedClasses.get();

  6.                if (classes == null) {

  7.                    classes = loadExtensionClasses();

  8.                    cachedClasses.set(classes);

  9.                }

  10.            }

  11.        }

  12.        return classes;

  13.    }

首先会从cachedClasses缓存加载扩展点配置,如果不存在,使用loadExtensionClasses从文件中加载扩展点,完成后缓存

 
   
   
 
  1. private Map<String, Class<?>> loadExtensionClasses() {

  2.        final SPI defaultAnnotation = type.getAnnotation(SPI.class);

  3.        if(defaultAnnotation != null) {

  4.            String value = defaultAnnotation.value();

  5.            if(value != null && (value = value.trim()).length() > 0) {

  6.                String[] names = NAME_SEPARATOR.split(value);

  7.                if(names.length > 1) {

  8.                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()

  9.                            + ": " + Arrays.toString(names));

  10.                }

  11.                if(names.length == 1) cachedDefaultName = names[0];

  12.            }

  13.        }

  14.        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();

  15.        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);

  16.        loadFile(extensionClasses, DUBBO_DIRECTORY);

  17.        loadFile(extensionClasses, SERVICES_DIRECTORY);

  18.        return extensionClasses;

  19.    }

在loadExtensionClasses方法中,首先根据@SPI注解来设置默认的扩展点实现 然后在通过loadFile从各个路径的文件加载扩展点配置

 
   
   
 
  1. private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {

  2.        String fileName = dir + type.getName();

  3.        try {

  4.            Enumeration<java.net.URL> urls;

  5.            ClassLoader classLoader = findClassLoader();

  6.            if (classLoader != null) {

  7.                urls = classLoader.getResources(fileName);

  8.            } else {

  9.                urls = ClassLoader.getSystemResources(fileName);

  10.            }

  11.            if (urls != null) {

  12.                while (urls.hasMoreElements()) {

  13.                    java.net.URL url = urls.nextElement();

  14.                    try {

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

  16.                        try {

  17.                            String line = null;

  18.                            while ((line = reader.readLine()) != null) {

  19.                                final int ci = line.indexOf('#');

  20.                                if (ci >= 0) line = line.substring(0, ci);

  21.                                line = line.trim();

  22.                                if (line.length() > 0) {

  23.                                    try {

  24.                                        String name = null;

  25.                                        int i = line.indexOf('=');

  26.                                        if (i > 0) {

  27.                                            name = line.substring(0, i).trim();

  28.                                            line = line.substring(i + 1).trim();

  29.                                        }

  30.                                        if (line.length() > 0) {

  31.                                            Class<?> clazz = Class.forName(line, true, classLoader);

  32.                                            //如果内部配置类不是该接口的实现,抛出异常

  33.                                            if (! type.isAssignableFrom(clazz)) {

  34.                                                throw new IllegalStateException("Error when load extension class(interface: " +

  35.                                                        type + ", class line: " + clazz.getName() + "), class "

  36.                                                        + clazz.getName() + "is not subtype of interface.");

  37.                                            }

  38.                                            //如果这个扩展点实现有@Adaptive注解,说明是适配类,缓存到cachedAdaptiveClass

  39.                                            //如果同一个扩展点有两个适配类,抛出异常

  40.                                            if (clazz.isAnnotationPresent(Adaptive.class)) {

  41.                                                if(cachedAdaptiveClass == null) {

  42.                                                    cachedAdaptiveClass = clazz;

  43.                                                } else if (! cachedAdaptiveClass.equals(clazz)) {

  44.                                                    throw new IllegalStateException("More than 1 adaptive class found: "

  45.                                                            + cachedAdaptiveClass.getClass().getName()

  46.                                                            + ", " + clazz.getClass().getName());

  47.                                                }

  48.                                            } else {

  49.                                                //不是适配类的逻辑

  50.                                                try {

  51.                                                    //判断是不是包装类

  52.                                                    //逻辑是看有没有以当前扩展点为参数的构造函数

  53.                                                    //如果是包装类,缓存到cachedWrapperClasses

  54.                                                    //包装类可以有多个,可以包装多次

  55.                                                    clazz.getConstructor(type);

  56.                                                    Set<Class<?>> wrappers = cachedWrapperClasses;

  57.                                                    if (wrappers == null) {

  58.                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();

  59.                                                        wrappers = cachedWrapperClasses;

  60.                                                    }

  61.                                                    wrappers.add(clazz);

  62.                                                } catch (NoSuchMethodException e) {

  63.                                                    //不是适配类 也不是 包装类 那就是普通扩展点实现类

  64.                                                    clazz.getConstructor();

  65.                                                    //下面这部分逻辑用于当配置文件中不存在扩展点名的时候,生成扩展点名的逻辑

  66.                                                    if (name == null || name.length() == 0) {

  67.                                                        name = findAnnotationName(clazz);

  68.                                                        if (name == null || name.length() == 0) {

  69.                                                            if (clazz.getSimpleName().length() > type.getSimpleName().length()

  70.                                                                    && clazz.getSimpleName().endsWith(type.getSimpleName())) {

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

  72.                                                            } else {

  73.                                                                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);

  74.                                                            }

  75.                                                        }

  76.                                                    }

  77.                                                    //扩展点名可能有多个

  78.                                                    String[] names = NAME_SEPARATOR.split(name);

  79.                                                    if (names != null && names.length > 0) {

  80.                                                        Activate activate = clazz.getAnnotation(Activate.class);

  81.                                                        if (activate != null) {

  82.                                                            //缓存<扩展点名,@Activate>

  83.                                                            //只缓存第一个扩展点名

  84.                                                            cachedActivates.put(names[0], activate);

  85.                                                        }

  86.                                                        for (String n : names) {

  87.                                                            //这个缓存只缓存第一个扩展点

  88.                                                            if (! cachedNames.containsKey(clazz)) {

  89.                                                                cachedNames.put(clazz, n);

  90.                                                            }

  91.                                                            Class<?> c = extensionClasses.get(n);

  92.                                                            if (c == null) {

  93.                                                                //这个缓存 缓存所有扩展点名 可以对应同一个Class

  94.                                                                extensionClasses.put(n, clazz);

  95.                                                            } else if (c != clazz) {

  96.                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());

  97.                                                            }

  98.                                                        }

  99.                                                    }

  100.                                                }

  101.                                            }

  102.                                        }

  103.                                    } catch (Throwable t) {

  104.                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);

  105.                                        exceptions.put(line, e);

  106.                                    }

  107.                                }

  108.                            } // end of while read lines

  109.                        } finally {

  110.                            reader.close();

  111.                        }

  112.                    } catch (Throwable t) {

  113.                        logger.error("Exception when load extension class(interface: " +

  114.                                            type + ", class file: " + url + ") in " + url, t);

  115.                    }

  116.                } // end of while urls

  117.            }

  118.        } catch (Throwable t) {

  119.            logger.error("Exception when load extension class(interface: " +

  120.                    type + ", description file: " + fileName + ").", t);

  121.        }

  122.    }

loadFile用来加载文件中扩展点配置,大致逻辑如下

  1. 在类路径或者jar包的指定文件夹查找以类全限定名为名字的文件

  2. 按行解析文件,格式为扩展点名 = 扩展点实现类

  3. 解析该类是否为适配类(这个扩展点实现有@Adaptive注解),如果是,赋值到cachedAdaptiveClass

  4. 解析该类是否为包装类(这个扩展点实现有以扩展点接口为参数的参数),如果是,放入cachedWrapperClasses集合

  5. 解析该类为普通扩展点实现类,解析类的@Activate注解,缓存到cachedActivates,缓存扩展点实现类与第一个扩展点名到cachedNames,扩展点名与扩展点实现放入extensionClasses集合

扩展点一些特性

扩展点自动包装

通过装饰者模式,使用包装类包装原始的扩展点实现,在原始扩展点实现前后插入其他逻辑,实现aop功能。 在解析扩展点配置的时候,我们会从配置中找到这些包装类并且缓存到cachedWrapperClasses,然后在创建扩展点实现的时候,使用这些包装类依次包装原始扩展点 自动包装功能在创建扩展点实例的方法内

 
   
   
 
  1. private T createExtension(String name) {

  2.        Class<?> clazz = getExtensionClasses().get(name);

  3.        if (clazz == null) {

  4.            throw findException(name);

  5.        }

  6.        try {

  7.            T instance = (T) EXTENSION_INSTANCES.get(clazz);

  8.            if (instance == null) {

  9.                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());

  10.                instance = (T) EXTENSION_INSTANCES.get(clazz);

  11.            }

  12.            //依赖注入

  13.            injectExtension(instance);

  14.            //包装,类似AOP

  15.            Set<Class<?>> wrapperClasses = cachedWrapperClasses;

  16.            if (wrapperClasses != null && wrapperClasses.size() > 0) {

  17.                for (Class<?> wrapperClass : wrapperClasses) {

  18.                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));

  19.                }

  20.            }

  21.            return instance;

  22.        } catch (Throwable t) {

  23.            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +

  24.                    type + ")  could not be instantiated: " + t.getMessage(), t);

  25.        }

  26.    }

我们可以看到,一旦cachedWrapperClasses不为空,会通过wrapperClass.getConstructor(type).newInstance(instance)循环包装原始实现,从而实现了Aop的功能

扩展点自动装配

在扩展点内可能会依赖其他扩展点,或者Spring容器内的bean,SPI提供了自动注入这些依赖的功能。主要通过解析setter方法,并且通过objectFactory取得这些依赖并注入 功能实现在injectExtension,在创建扩展点实例的时候会被调用

 
   
   
 
  1. private T injectExtension(T instance) {

  2.        try {

  3.            if (objectFactory != null) {

  4.                for (Method method : instance.getClass().getMethods()) {

  5.                    if (method.getName().startsWith("set")

  6.                            && method.getParameterTypes().length == 1

  7.                            && Modifier.isPublic(method.getModifiers())) {

  8.                        Class<?> pt = method.getParameterTypes()[0];

  9.                        try {

  10.                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";

  11.                            Object object = objectFactory.getExtension(pt, property);

  12.                            if (object != null) {

  13.                                method.invoke(instance, object);

  14.                            }

  15.                        } catch (Exception e) {

  16.                            logger.error("fail to inject via method " + method.getName()

  17.                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);

  18.                        }

  19.                    }

  20.                }

  21.            }

  22.        } catch (Exception e) {

  23.            logger.error(e.getMessage(), e);

  24.        }

  25.        return instance;

  26.    }

在解析setter方法之后,得到setter方法的注入类型,然后通过objectFactory提取实现 这个objectFactory在ExtensionLoader的构造函数中设置过,具体实现为ExtensionFactory的适配类AdaptiveExtensionFactory

 
   
   
 
  1. objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

我们看下适配类的实现

 
   
   
 
  1. @Adaptive

  2. public class AdaptiveExtensionFactory implements ExtensionFactory {

  3.    private final List<ExtensionFactory> factories;

  4.    public AdaptiveExtensionFactory() {

  5.        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);

  6.        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();

  7.        for (String name : loader.getSupportedExtensions()) {

  8.            list.add(loader.getExtension(name));

  9.        }

  10.        factories = Collections.unmodifiableList(list);

  11.    }

  12.    public <T> T getExtension(Class<T> type, String name) {

  13.        for (ExtensionFactory factory : factories) {

  14.            T extension = factory.getExtension(type, name);

  15.            if (extension != null) {

  16.                return extension;

  17.            }

  18.        }

  19.        return null;

  20.    }

  21. }

可以看到在构造函数中会把所有ExtensionFactory的实现缓存起来,然后在getExtension的时候,依次调用ExtensionFactory的普通扩展点的getExtension方法来获取依赖 ExtensionFactory的普通实现有

 
   
   
 
  1. spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory

  2. spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

spi和spring实现分别代表从ExtensionLoader和Spring容器获取对应依赖 需要注意一下spi的实现类中

 
   
   
 
  1. public <T> T getExtension(Class<T> type, String name) {

  2.        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {

  3.            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);

  4.            if (loader.getSupportedExtensions().size() > 0) {

  5.                return loader.getAdaptiveExtension();

  6.            }

  7.        }

  8.        return null;

  9.    }

注入的为适配器类,不是具体普通实现类

扩展点自适应

扩展点自适应的意思是,扩展点会在程序运行时根据Url内的参数自动选择对应的实现进行调用,采用适配器类实现,这也是一个装饰者模式。 每个扩展点必须有一个适配类,如果没有,框架会通过Javaassist自动创建一个,但是有一个前提,需要自动适配的接口方法需要使用@adaptive注解 获取适配对象的方法如下

 
   
   
 
  1. public T getAdaptiveExtension() {

  2.        Object instance = cachedAdaptiveInstance.get();

  3.        if (instance == null) {

  4.            if(createAdaptiveInstanceError == null) {

  5.                synchronized (cachedAdaptiveInstance) {

  6.                    instance = cachedAdaptiveInstance.get();

  7.                    if (instance == null) {

  8.                        try {

  9.                            instance = createAdaptiveExtension();

  10.                            cachedAdaptiveInstance.set(instance);

  11.                        } catch (Throwable t) {

  12.                            createAdaptiveInstanceError = t;

  13.                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);

  14.                        }

  15.                    }

  16.                }

  17.            }

  18.            else {

  19.                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);

  20.            }

  21.        }

  22.        return (T) instance;

  23.    }

这边会对适配类实例做一个缓存,如果缓存不存在,调用createAdaptiveExtension创建

 
   
   
 
  1. private T createAdaptiveExtension() {

  2.        try {

  3.            return injectExtension((T) getAdaptiveExtensionClass().newInstance());

  4.        } catch (Exception e) {

  5.            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);

  6.        }

  7.    }

createAdaptiveExtension方法中通过getAdaptiveExtensionClass来取得对应的适配器类,然后调用newInstance实例化,在通过injectExtension注入依赖后返回

 
   
   
 
  1. private Class<?> getAdaptiveExtensionClass() {

  2.        getExtensionClasses();

  3.        if (cachedAdaptiveClass != null) {

  4.            return cachedAdaptiveClass;

  5.        }

  6.        return cachedAdaptiveClass = createAdaptiveExtensionClass();

  7.    }

在getAdaptiveExtensionClass方法中,首先会调用getExtensionClasses方法初始化,如果在初始化过程中找不到适配类,那么通过createAdaptiveExtensionClass方法自己创建适配类

 
   
   
 
  1. private Class<?> createAdaptiveExtensionClass() {

  2.        String code = createAdaptiveExtensionClassCode();

  3.        ClassLoader classLoader = findClassLoader();

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

  5.        return compiler.compile(code, classLoader);

  6.    }

这边会先调用createAdaptiveExtensionClassCode方法拼接具体适配类的代码,然后在使用Compiler实现去生成Class对象,Compiler也使用了扩展点,默认使用javaassist实现 看下createAdaptiveExtensionClassCode方法生成的适配类的代码样式

 
   
   
 
  1. package com.alibaba.dubbo.rpc;

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

  3. public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {

  4.    public void destroy() {

  5.        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");

  6.    }

  7.    public int getDefaultPort() {

  8.        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");

  9.    }

  10.    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {

  11.        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");

  12.        if (arg0.getUrl() == null)

  13.            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");

  14.        com.alibaba.dubbo.common.URL url = arg0.getUrl();

  15.        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());

  16.        if (extName == null)

  17.            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

  18.        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

  19.        return extension.export(arg0);

  20.    }

  21.    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {

  22.        if (arg1 == null) throw new IllegalArgumentException("url == null");

  23.        com.alibaba.dubbo.common.URL url = arg1;

  24.        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());

  25.        if (extName == null)

  26.            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

  27.        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

  28.        return extension.refer(arg0, arg1);

  29.    }

  30. }

大致的逻辑和开始说的一样,通过url解析出参数,解析的逻辑由@Adaptive的value参数控制,然后再根据得到的扩展点名获取扩展点实现,然后进行调用 具体拼接逻辑大家可以看createAdaptiveExtensionClassCode的实现

扩展点自动激活

对于集合类扩展点,比如Filter,需要一次性获取多个实现进行链式调用,这里用到了扩展点自动激活,对于有@Activate注解的接口类,即使不显示通过扩展名获取,如果url里面含有@Activate的value配置的参数,也能获取到这个扩展点 我们通过getActivateExtension这个来获取多个扩展点,values是使用者主动设置的扩展点名

 
   
   
 
  1. public List<T> getActivateExtension(URL url, String[] values, String group) {

  2.        List<T> exts = new ArrayList<T>();

  3.        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);

  4.        if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {

  5.            getExtensionClasses();

  6.            //这边是扩展点自动激活的逻辑,除去values里面配置的扩展点

  7.            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {

  8.                String name = entry.getKey();

  9.                Activate activate = entry.getValue();

  10.                if (isMatchGroup(group, activate.group())) {

  11.                    T ext = getExtension(name);

  12.                    //除去values里面配置的扩展点,其他的扩展点自动激活

  13.                    if (! names.contains(name)

  14.                            && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)

  15.                            //这里来过滤@activate里面配置的value是否在url里面出现

  16.                            && isActive(activate, url)) {

  17.                        exts.add(ext);

  18.                    }

  19.                }

  20.            }

  21.            Collections.sort(exts, ActivateComparator.COMPARATOR);

  22.        }

  23.        List<T> usrs = new ArrayList<T>();

  24.        //加载value所对应的扩展点实现类

  25.        for (int i = 0; i < names.size(); i ++) {

  26.            String name = names.get(i);

  27.            if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)

  28.                    && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {

  29.                if (Constants.DEFAULT_KEY.equals(name)) {

  30.                    if (usrs.size() > 0) {

  31.                        exts.addAll(0, usrs);

  32.                        usrs.clear();

  33.                    }

  34.                } else {

  35.                    T ext = getExtension(name);

  36.                    usrs.add(ext);

  37.                }

  38.            }

  39.        }

  40.        if (usrs.size() > 0) {

  41.            exts.addAll(usrs);

  42.        }

  43.        return exts;

  44.    }

这个方法得到扩展实现分为两部分,第一部分是自动激活的,第二部分是用户指定的。 自动激活也不是无条件的,首先group参数要符合要求,其次的话就是Url参数里面需要有@Activate注解value属性对应的参数,只需要存在一个即可,判断逻辑如下

 
   
   
 
  1. private boolean isActive(Activate activate, URL url) {

  2.        String[] keys = activate.value();

  3.        if (keys == null || keys.length == 0) {

  4.            return true;

  5.        }

  6.        for (String key : keys) {

  7.            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {

  8.                String k = entry.getKey();

  9.                String v = entry.getValue();

  10.                if ((k.equals(key) || k.endsWith("." + key))

  11.                        && ConfigUtils.isNotEmpty(v)) {

  12.                    return true;

  13.                }

  14.            }

  15.        }

  16.        return false;

  17.    }

dubbo中扩展点自动激活最常使用的扩展点为Filter,调用getActivateExtension方法在ProtocolFilterWrapper的buildInvokerChain中

 
   
   
 
  1. private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {

  2.        Invoker<T> last = invoker;

  3.        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

  4.        if (filters.size() > 0) {

  5.            for (int i = filters.size() - 1; i >= 0; i --) {

  6.                final Filter filter = filters.get(i);

  7.                final Invoker<T> next = last;

  8.                last = new Invoker<T>() {

  9.                    public Class<T> getInterface() {

  10.                        return invoker.getInterface();

  11.                    }

  12.                    public URL getUrl() {

  13.                        return invoker.getUrl();

  14.                    }

  15.                    public boolean isAvailable() {

  16.                        return invoker.isAvailable();

  17.                    }

  18.                    public Result invoke(Invocation invocation) throws RpcException {

  19.                        return filter.invoke(next, invocation);

  20.                    }

  21.                    public void destroy() {

  22.                        invoker.destroy();

  23.                    }

  24.                    @Override

  25.                    public String toString() {

  26.                        return invoker.toString();

  27.                    }

  28.                };

  29.            }

  30.        }

  31.        return last;

  32.    }

这个方法会构造一个拦截器链,拦截器链的末尾是我们需要调用的方法,即protocol的refer和export方法 这样,通过自定义filter我们实现日志记录,错误结果封装等功能


以上是关于Dubbo之SPI源码分析的主要内容,如果未能解决你的问题,请参考以下文章

dubbo源码分析之基于SPI的强大扩展

Dubbo SPI 源码深入分析

Dubbo源码分析之ExtensionLoader加载过程解析

Apache Dubbo 之 内核剖析

Apache Dubbo 之 内核剖析

Apache Dubbo 之 内核剖析