【Dubbo笔记】dubbo SPI、Adaptive机制解读

    在很多场合,系统需要根据上下文、参数决定逻辑。例如支付,使用微信、支付宝、银联支付逻辑不一样,比较直接的写法是用 【if】判断,走不同的分支;另外,设计模式里面有一种工厂模式也适用这种场景。【Dubbo】里也有很多这种场景(多协议等等),但是【Dubbo】使用SPI(Service Provider Interface) + Adaptive机制来应付这种场景。这种机制更灵活、更具扩展性。

1 Dubbo SPI

    区别于JDK原生的SPI,【Dubbo】自己实现了一套SPI机制实时加载具体的实现类。org.apache.dubbo.common.extension.ExtensionLoader类是Dubbo SPI机制的核心,看一下【ExtensionLoader】的几个核心方法。

public class ExtensionLoader<T> {
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);

    private final Class<?> type;
    private final ExtensionFactory objectFactory;
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    private volatile Class<?> cachedAdaptiveClass = null;
    private String cachedDefaultName;
    private Set<Class<?>> cachedWrapperClasses;

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {}

    /**
     * Get activate extensions.
     *
     * @param url    url
     * @param values extension point names
     * @param group  group
     * @return extension list which are activated
     * @see org.apache.dubbo.common.extension.Activate
     */
    public List<T> getActivateExtension(URL url, String[] values, String group) {}

    /**
     * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
     * will be thrown.
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {}

    @SuppressWarnings("unchecked")
    public T getAdaptiveExtension(){}
}

【ExtensionLoader】使用了单例模式,既有静态成员、又有实例成员。

  1.1 【ExtensionLoader】静态成员

  • EXTENSION_LOADERS 静态变量 ConcurrentHashMap 用来存储各SPI接口对应【ExtensionLoader】实例,每个SPI接口的ExtensionLoader都是一个单例,key是SPI接口类;
  • EXTENSION_INSTANCES 静态变量 ConcurrentHashMap 用来存储所有已加载的实现类的实例, key是实现类;
  • <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)  静态方法 获取SPI接口(type)的ExtensionLoader实例;
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
            if (type == null) {
                throw new IllegalArgumentException("Extension type == null");
            }
            //笔者注  不是接口类型 不允许创建ExtensionLoader
            if (!type.isInterface()) {
                throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
            }
            //笔者注  没有@SPI注解 也不允许创建ExtensionLoader
            if (!withExtensionAnnotation(type)) {
                throw new IllegalArgumentException("Extension type (" + type +
                        ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
            }
            //笔者注  如果有创建过则直接使用,没有则创建一个ExtensionLoader实例
            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;
        }
     

  1.2 【ExtensionLoader】实例成员

  • type  实例变量 当前【ExtensionLoader】实例关联的SPI接口;

  • objectFactory 实例变量 ExtensionFactory 用来给SPI接口实例自动注入属性,类似于Spring框架的IOC;
  • cachedNames 实例变量 ConcurrentMap 缓存实现类对应的扩展名 
  • cachedClasses 实例变量 用来缓存扩展名对应的实现类
  • cachedActivates 实例变量 ConcurrentHashMap  如果实现类有@Activate注解,则将注解缓存下来
  • cachedInstances 实例变量 ConcurrentHashMap 用来缓存扩展名对应的实现类实例
  • cachedAdaptiveInstance 实例变量 用来缓存SPI接口的Adaptive类的实例
  • cachedAdaptiveClass 实例变量  用来缓存SPI接口的Adaptive类
  • cachedDefaultName 实例变量 用来缓存SPI接口的默认实现类的扩展名,注解@SPI的value
  • cachedWrapperClasses 实例变量 缓存SPI接口的包装类
  • T getExtension(String name) 实例方法 获取扩展名[name]对应的实现类实例  

    从磁盘中(META-INF/dubbo/external、META-INF/dubbo/internal、META-INF/dubbo、META-INF/services)加载对应SPI接口的实现类,找到指定扩展名[name]的类,创建实例并自动注入相关属性。

    自动注入只对有且仅有一个参数且参数类型不是基本类型的setter方法,AdaptiveExtensionFactory.getExtension,通过实现类SpringExtensionFactory跟Spring容器结合起来注入Spring的Bean。

public class ExtensionLoader<T> {
    
    //笔者注:如果已经加载过则使用,否则加载实现类并返回name对应的实例
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }

        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {

                    //笔者注 当前是loader中没有缓存,则去创建
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

    //笔者注 创建name对应扩展实例
    private T createExtension(String name) {
        //笔者注 获取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);
            //笔者注 如果有包装类 则包装对应的实例
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
             
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

    // 笔者注: 对实例自动注入属性,只对只有一个参数且参数类型不是基本类型(Integer、Double等)的setter方法注入。
    //注入的对象是通过ExtensionFactory的Adaptive类AdaptiveExtensionFactory获取,
    //AdaptiveExtensionFactory跟Spring的容器结合起来了
    private T injectExtension(T instance) {

        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    String property = getSetterProperty(method);
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
                    logger.error("Failed to inject via method " + method.getName()
                            + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }

            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
    
    //笔者注 取SPI接口的实现类,首先从缓存取,取不到则从磁盘文件里加载实现类
    //(META-INF/dubbo/external、META-INF/dubbo/internal、META-INF/dubbo、META-INF/services)
    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;
    }
}

 

  • T getAdaptiveExtension() 实例方法  获取SPI接口对应的Adaptive类的实例。

     Dubbo有两种方法创建Adaptive类,其一是在某个实现类上添加注解@Adaptive则表明这个方法就是该SPI接口的Adaptive类,其二是Dubbo自动生成一个Adaptive类文件并编译成class对象。

    自动生成的Adaptive类只对SPI接口中有@Adaptive注解的方法做处理,没有@Adaptive注解的方法生成的方法体直接是"throw new UnsupportedOperationException"。有注解的方法生成的方法体实现的逻辑是:

  1.  找URL对象:类型URL的参数,没有则在其它参数中寻找"返回类型为URL"的方法并获得URL对象,没有则报错。
  2. 在URL中依次找keys集合中的key对应的value作为实际扩展名,都没找到则使用SPI接口默认扩展名;keys集合来源于方法上@Adaptive注解的value数组;如果数组为空,则用SPI接口的类名(驼峰结构转为xx.yy.zz形式);实际的代码形如String extName = url.getParameter("key1", url.getParameter("key2", url.getParameter("key3", "a")))。
  3. 利用【ExtensionLoader】获取扩展名对应的实现类实例。
  4. 调用实际实例的方法,返回结果。
public class ExtensionLoader<T> {

    //笔者注 获取SPI接口对应的Adaptive类的实例,先从缓存中取,没有则自动生成一个Adaptive类创建实例
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }

    @SuppressWarnings("unchecked")
    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
   
   
   private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        
        //笔者注 没有则自动生成一个Adaptive类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

   private Class<?> createAdaptiveExtensionClass() {
        //笔者注 自动生成一个Adaptive类
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }
}

public class AdaptiveClassCodeGenerator {
    private static final String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n";

    private static final String CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n";

    private static final String CODE_METHOD_ARGUMENT = "%s arg%d";

    private static final String CODE_METHOD_THROWS = "throws %s";

    private static final String CODE_UNSUPPORTED = "throw new UnsupportedOperationException(\"The method %s of interface %s is not adaptive method!\");\n";

    private static final String CODE_URL_NULL_CHECK = "if (arg%d == null) throw new IllegalArgumentException(\"url == null\");\n%s url = arg%d;\n";

    private static final String CODE_EXT_NAME_ASSIGNMENT = "String extName = %s;\n";
    
    //笔者注 生成Adaptive类,拼接类的字符串,然后编译加载
    public String generate() {
        // no need to generate adaptive class since there's no adaptive method found.
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
        }

        StringBuilder code = new StringBuilder();
        code.append(generatePackageInfo());
        code.append(generateImports());
        code.append(generateClassDeclaration());

        Method[] methods = type.getMethods();
        for (Method method : methods) {
            code.append(generateMethod(method));
        }
        code.append("}");

        if (logger.isDebugEnabled()) {
            logger.debug(code.toString());
        }
        return code.toString();
    }
}

    

  • List<T> getActivateExtension(URL url, String[] values, String group) 实例方法 获取激活的实现类实例,values是扩展名列表("-name"表示剔除该扩展名对应的实现类)、group一般就是provider(提供方)、consumer(消费方)

    逻辑如下 

  1.  如果不剔除默认(即values[]没有"-default")则先收集[默认激活实例];  
  2. 依次获取values中实现类的实例("-name"表示剔除name对应的实例,即不获取);
  3. 根据"default"在values位置确定[默认激活实例]的位置,如果没有"default"则表示[默认激活实例]在最前面,values[]里面对应的实例依次放在后面;
public class ExtensionLoader<T> {

    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> activateExtensions = new ArrayList<>();
        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
        
        //笔者注 如果不剔除默认(即没有"-default")则先收集[默认激活实例]
        //[默认激活实例]有几个特点
        //(1)实现类上有@Activate注解且跟group和url匹配 
        //(2)实现类扩展名不在values中("-name"或name)
        if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            getExtensionClasses();
            for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                String name = entry.getKey();
                Object activate = entry.getValue();

                String[] activateGroup, activateValue;

                if (activate instanceof Activate) {
                    activateGroup = ((Activate) activate).group();
                    activateValue = ((Activate) activate).value();
                } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                    activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                    activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                } else {
                    continue;
                }
                if (isMatchGroup(group, activateGroup)
                        && !names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        && isActive(activateValue, url)) {
                    activateExtensions.add(getExtension(name));
                }
            }
            activateExtensions.sort(ActivateComparator.COMPARATOR);
        }

        //笔者注 依次获取values中实现类的实例(-name表示剔除,不获取)。根据"default"位置决定默认[激活实例]位置
        List<T> loadedExtensions = new ArrayList<>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            if (!name.startsWith(REMOVE_VALUE_PREFIX)
                    && !names.contains(REMOVE_VALUE_PREFIX + name)) {

                //笔者注 如果是"default"说明之前的实现类实例,要放在[默认激活实例]之前
                if (DEFAULT_KEY.equals(name)) {
                    if (!loadedExtensions.isEmpty()) {
                        activateExtensions.addAll(0, loadedExtensions);
                        loadedExtensions.clear();
                    }
                } else {
                    loadedExtensions.add(getExtension(name));
                }
            }
        }
        //其余的 放在[默认激活实例]之后
        if (!loadedExtensions.isEmpty()) {
            activateExtensions.addAll(loadedExtensions);
        }
        return activateExtensions;
    }
}

 

   

2 SPI在dubbo中应用

    【Dubbo】广泛使用SPI机制,所有@SPI注解的接口都是一个SPI扩展点,如下图:

3 示例

    1 首先定义一个@SPI注解的接口,默认实现类是"a"

package com.focuse.jdkdemo.dubbospi;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

@SPI("a")
public interface DubboSpiTest {
    @Adaptive
    String read(URL url);

    void write(String a);
}

    2 定义2个实现类DubboSpiTestImplA、DubboSpiTestImplB

package com.focuse.jdkdemo.dubbospi;

import org.apache.dubbo.common.URL;

public class DubboSpiTestImplA implements DubboSpiTest{

    public String read(URL url) {
        return "Hello A!!";
    }

    public void write(String a){

    }
}
package com.focuse.jdkdemo.dubbospi;

import org.apache.dubbo.common.URL;

public class DubboSpiTestImplB implements DubboSpiTest{

    public String read(URL url) {
        return "Hello B!!";
    }

    public void write(String a){

    }
}

3 创建spi文件 META-INF/dubbo/com.focuse.jdkdemo.dubbospi.DubboSpiTest

a=com.focuse.jdkdemo.dubbospi.DubboSpiTestImplA
b=com.focuse.jdkdemo.dubbospi.DubboSpiTestImplB

4 ExtensinoLoader获取DubboSpiTest的Adaptive类的实例

package com.focuse.jdkdemo.dubbospi;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;

import java.util.HashMap;

/**
 * @author :
 * @date :Created in 2020/6/6 下午3:08
 * @description:
 * @modified By:
 */
public class DubboSpiApplication {


    public static void main(String[] args) throws Exception{
        //获取DubboSpiTest接口的adaptive类
        DubboSpiTest dubboSpiTest = ExtensionLoader.getExtensionLoader(DubboSpiTest.class).getAdaptiveExtension();
        HashMap<String, String> params = new HashMap<>();
        //上下文中指定实现类,因为DubboSpiTest.read方法上注解@Adaptive没有value值,
        //所以上下文的key就是DubboSpiTest类名(驼峰转xxx.yyy.zz形式)
        params.put("dubbo.spi.test", "b");
        URL context = new URL("dubbo", "DubboSpiTest", 0, params);
        String result = dubboSpiTest.read(context);
        System.out.println("*************************");
        System.out.println(result);
        System.out.println("*************************");
    }
}

在上下文中指定类实现类用"b"(因为DubboSpiTest.read方法上注解@Adaptive没有value值,所以上下文的key就是DubboSpiTest类名(驼峰转xxx.yyy.zz形式)),所以实际调用的是DubboSpiTestImplB.read()

如果不指定,则会使用默认的"a"

DubboSpiTest$Adaptive是自动生成的DubboSpiTest的Adaptive类,扩展名先从上下文中取,没有则用默认的"a",拿到扩展名后再获取扩展名对应的实例。

package com.focuse.jdkdemo.dubbospi;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class DubboSpiTest$Adaptive implements com.focuse.jdkdemo.dubbospi.DubboSpiTest {
    public void write(java.lang.String arg0) {
        throw new UnsupportedOperationException("The method public abstract void com.focuse.jdkdemo.dubbospi.DubboSpiTest.write(java.lang.String) of interface com.focuse.jdkdemo.dubbospi.DubboSpiTest is not adaptive method!");
    }

    public java.lang.String read(org.apache.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("dubbo.spi.test", "a");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (com.focuse.jdkdemo.dubbospi.DubboSpiTest) name from url (" + url.toString() + ") use keys([dubbo.spi.test])");
        com.focuse.jdkdemo.dubbospi.DubboSpiTest extension = (com.focuse.jdkdemo.dubbospi.DubboSpiTest) ExtensionLoader.getExtensionLoader(com.focuse.jdkdemo.dubbospi.DubboSpiTest.class).getExtension(extName);
        return extension.read(arg0);
    }
}

 

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