Dubbo中的SPI机制源码解析

本文深入剖析Dubbo的SPI机制,介绍了SPI的概念及其在Dubbo中的应用,包括自适应拓展机制、扩展点源码解析等内容,揭示了Dubbo如何通过SPI机制实现灵活的服务发现与扩展。

SPI机制简介

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。

在源码阅读之前必须要理解的SPI机制中的注解。

@SPI 这个注解标识这是spi的一个扩展点,实现这个接口可以增加一个扩展机制,这个注解可以有默认使用的实现。例如@SPI(“spring”)默认使用spring容器。

@Adaptive 这个注解表明的是自适应的扩展类。Adaptive 可注解在类或方法上。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive 是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。

@Active 注解,标识激活的扩展类,可以设置只作用到某些角色上。比如只作用到消费者,或者生产者上。

自适应拓展机制介绍

该机制的官网介绍可以点击查看。下面简单描述一下我的理解:
每一个SPI拓展接口都可能会有很多种实现,不同的接口实现在不同的场景可能会用到,也可能用不到,那就不能一次性全部加载进来,不然会浪费资源。而且,具体使用哪个接口实现是运行时后才知道的,也就意味着,没办法提前知道只加载那些用到的实现。dubbo的自适应拓展机制就时为了解决这个问题,思路以Protoccol接口为例:

  1. Protoccol 接口有 DubboProtoccol RedisProtoccol HttpProtoccol等多种实现,现在需要一个中间类,对于用户来说是无感知的,这个类的方法调用能够通过传入的参数识别到用的是哪种具体实现,创建这个类就时自适应拓展机制的关键。
  2. 这个类对用户来说时无感知,那就是面向原有的接口,所以这个类应该实现Protoccol 接口,这样用户调用接口的方法返回具体实现就行。
  3. 这个类需要动态生成或者手动编码,这个类上就是 @Adaptive修饰的类或者类中的方法被这个注解修饰。

扩展点源码阅读

1.获取扩展的方法

dubbo中所有的SPI扩展点接口都被解析封装成一个 ExtensionLoader 对象,该对象持有扩展点的详细信息。获取具体的扩展点就是获取这个Loader对象。获取扩展对象的方法如下:

//TODO 获取spi扩展点
 @SuppressWarnings("unchecked")
 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
     if (type == null) {
         throw new IllegalArgumentException("Extension type == null");
     }
     if (!type.isInterface()) {
         throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
     }
     //判断是不是 SPI 注解的扩展点
     if (!withExtensionAnnotation(type)) {
         throw new IllegalArgumentException("Extension type (" + type +
                 ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
     }
     //先从缓存中获取扩展点
     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注解标识的接口类对象,重点查看 new ExtensionLoader(type) 方法。

private ExtensionLoader(Class<?> type) {
        this.type = type;
        //先获取ExtensionFactory的所有扩展点,再从中获取自适应的扩展点loader
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

这里会将扩展的接口和扩展加载的类会被 ExtensionLoader 对象所持有。看getAdaptiveExtension方法实现:

//TODO 获取自适应扩展机制示例(每一个spi扩展点对应一个实例)
@SuppressWarnings("unchecked")
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;
}

再查看 createAdaptiveExtension方法:

//TODO 创建自适应扩展点实例
@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);
    }
}

这个方法中先获取到了扩展类的Class对象,然后通过反射传教扩展类对象,最后注入这个类依赖的对象。
看看getAdaptiveExtensionClass如何创建对象:

private Class<?> getAdaptiveExtensionClass() {
    //获取自适应扩展的所有实现类进行缓存
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //没有缓存则创建自适应扩展,通过代码生成类的字符串,再利用类加载器进行加载编译
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getExtensionClasses 方法

//TODO 对应扩展点的所有实现类,缓存到 Holder<Map<String, Class<?>>> cachedClasses中
private Map<String, Class<?>> getExtensionClasses() {
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                //加载spi所有的扩展类
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
        //缓存默认扩展名,SPI注解的value值,就是会使用的SPI实现
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        // directory: "META-INF/dubbo/internal/"  "META-INF/dubbo/"  "META-INF/services/"
        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

        return extensionClasses;
    }

strategies时静态变量加载进来的,硬编码写死了spi机制的加载路径
// directory: “META-INF/dubbo/internal/” “META-INF/dubbo/” “META-INF/services/”
就是这几个。可以通过看下面的方法查看。

//TODO 通过JDK自带SPI机制获取到 META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy 配置的加载策略
    //加载策略中有以后的资源加载路径 directory: "META-INF/dubbo/internal/"  "META-INF/dubbo/"  "META-INF/services/"
private static LoadingStrategy[] loadLoadingStrategies() {
     return stream(load(LoadingStrategy.class).spliterator(), false)
             .sorted()
             .toArray(LoadingStrategy[]::new);
 }

通过查看下面的目录可以到加载的实现类,adaptive的就是手动编码的自适应扩展类。目前只有两个类分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。其余的都是自动生成
在这里插入图片描述
动态生成的方法如下:

private Class<?> createAdaptiveExtensionClass() {
        //生成类的字符串
        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);
    }

就是生成含有adaptive注解的方法的类,然后动态编译加载。最后所有的属性缓存都在ExtensionLoader中:

public class ExtensionLoader<T> {

    private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);

    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

    //TODO 缓存每一个spi扩展点接口与对应扩展点实例的映射
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
    //TODO 扩展点接口类型
    private final Class<?> type;

    private final ExtensionFactory objectFactory;

    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    //TODO 缓存所有spi接口所有的普通扩展实现类与名称的映射,在META-INF文件中定义的,名称可以多个对应一个,逗号隔开
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    //TODO 缓存Activate注解的普通扩展类 Map<String, Object> cachedActivates
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    //TODO 自适应扩展类实例化对象缓存
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    //TODO 从MATE-INF 目录下加载的Adaptive注解标记的自适应扩展类,目前只有 AdaptiveCompiler 和 AdaptiveExtensionFactory
    private volatile Class<?> cachedAdaptiveClass = null;  //自适应扩展类,被Adaptive注解修饰
    //TODO @SPI注解的value值,默认使用的扩展类
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;
    //TODO 缓存的扩展类带有有参构造函数,是一个装饰类,缓存到Set<Class<?>> cachedWrapperClasses
    private Set<Class<?>> cachedWrapperClasses;

    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
    //TODO 通过jdk的SPI机制获取到加载策略,里面有加载spi的目录
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值