Dubbo SPI原理与设计精要

SPI全程Service Provider Interface,也就是服务发现机制。SPI分离服务接口和具体实现类,解耦服务调用方和实现者,提升应用框架可扩展性,实现可插拔微内核架构。SPI遵循基于接口编程而非具体实现,满足面向对象设计依赖倒置原则。同时,还支持开闭原则,也就是对扩展开放,对修改封闭。

在Java中,很多框架还使用SPI机制,如Spring框架、数据库驱动和日志接口,另外Dubbo还提供更高级SPI扩展机制。

01 Java SPI

1.1 Java SPI原理

Jdk 1.6引入SPI机制,提供数据库、日志框架和消息服务扩展接口,达到动态加载服务实现。底层原理主要包括四个步骤,比如服务接口、服务提供者、服务配置和服务加载器。

组件 描述
服务接口 定义服务抽象功能接口,规定服务提供者需要实现的功能
服务提供者 服务提供者实现服务接口,提供具体实现。例如,JDBC不同数据库驱动程序(MySQL、PostgreSQL等)提供各自实现类
配置文件 服务提供者在META-INF/services/ 目录下创建服务接口权限类名文件,然后写入服务提供者实现类的全限定类名到文件
服务加载器(ServiceLoader) 在Java中,提供ServiceLoader类扫描META-INF/services/目录配置文件,找到需要事务配置文件进行加载和实例化服务提供者实现类

1.2 Java SPI实现

1.2.1 服务接口

定义服务接口或业务抽象,规定服务提供者需要实现的功能。

java 复制代码
public interface PaymentService {
    void processPayment(double amount);
}

1.2.2 提供者服务实现

服务提供者实现服务接口,提供具体的实现。

java 复制代码
public class PayPalPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing payment via PayPal: $" + amount);
    }
}

public class CreditCardPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing payment via Credit Card: $" + amount);
    }
}

1.2.3 服务配置

接下来,创建 ETA-INF/services/com.feiyu.PaymentService 文件,文件回填服务实现类全限定类名,注意每个服务提供者实现类占据一行。

java 复制代码
com.feiyu.pay.PayPalPaymentService
com.feiyu.credit.CreditCardPaymentService

1.2.4 服务加载器

在Java中,提供 ServiceLoader 类扫描 META-INF/services/ 目录查找接口配置文件,然后动态加载和实例化服务提供者实现类。

java 复制代码
public class PaymentApp {
    public static void main(String[] args) {
        // 加载所有PaymentService实现
        ServiceLoader<PaymentService> services= ServiceLoader.load(PaymentService.class);
        // 遍历所有可用服务实现
        for (PaymentService service: services) {
            service.processPayment(100.0);
        }
    }
}

1.5 Java SPI总结

Java SPI底层通过java.util.ServiceLoader#load进行加载,每次调用重新扫描META-INF/services/目录服务接口配置文件返回循环迭代器,程序只需要循环调用Class.forName进行反射查找和实例化服务提供者即可。

通过加载逻辑直观看出存在一些弊端,比如重复加载不添加缓存存在性能开销,一次性加载全部服务提供者存在无效加载,以及不能指定特定服务提供者进行加载。

02 Dubbo SPI演进之路

Java SPI存在性能问题、无效加载和不能指定加载服务提供者,所以为了解决这些问题Dubbo重新设计一套SPI实现机制,而且还提供后置增强实例。Dubbo SPI底层提供指定参数条件、默认或者激活方式获取服务提供者(也即是,所谓的扩展类),实现服务提供者自动加载和切换,是构建可插拔、模块化服务架构的重要工具。

Dubbo SPI组件 Java SPI组件
扩展接口 服务接口
扩展类 服务提供者
扩展配置 服务配置
扩展加载器 服务加载器

03 Dubbo SPI规范

3.1 扩展工厂

Dubbo提供ExtensionFactory进行扩展类实例(setter)依赖注入处理,对扩展类实例进行加强。底层提供三种实现,包括AdaptiveExtensionFactory、SpiExtensionFactory和SpringExtensionFactory三种实现。

需要注意的是,扩展工厂底层是通过Java SPI进行服务发现加载,所以实现配置会全部进行加载。

java 复制代码
@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> type, String name);
}

3.2 扩展接口

Dubbo SPI约定服务接口必须满足两个条件,也就是接口类和接口被@SPI修饰。

java 复制代码
@SPI
public interface PaymentService {
    void processPayment(double amount);
}

3.3 扩展包装器

Dubbo SPI提供包装器进行扩展类加强,格式就是包装器必须提供扩展接口构造函数。另外,可以通过@Wrapper额外指定需要包装范围,默认不添加都需要包装增强。

java 复制代码
// 申明方式一
public class WrapperPaymentService implements PaymentService {
    private final PaymentService paymentService;

    public WrapperPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    @Override
    public void processPayment(double amount) {
        this.paymentService.processPayment(amount);
    }
}

// 申明方式二
@Wrapper
public class WrapperPaymentService implements PaymentService {
    private final PaymentService paymentService;

    public WrapperPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    @Override
    public void processPayment(double amount) {
        this.paymentService.processPayment(amount);
    }
}

public @interface Wrapper {
    // 指定需要包装的扩展名
    String[] matches() default {};

    // 排除不进行包装的扩展名
    String[] mismatches() default {};
}

3.4 配置默认扩展名

Dubbo SPI申明使用@SPI注解注释接口就是扩展接口,以及通过@SPI(value=?)注解指定值就是默认扩展名。

java 复制代码
@SPI(value="pay") // 指定默认扩展名为pay
public interface PaymentService {
    void processPayment(double amount);
}

3.5 自适应扩展类

Dubbo SPI机制提供@Adaptive注解注释自适应扩展类,每个扩展接口仅支持唯一一个自适应扩展类,所以,需要当心选择META-INF/dubbo/internal/目录是不支持覆盖处理的。

java 复制代码
@Adaptive
public class PayPalPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing payment via PayPal: $" + amount);
    }
}

3.6 激活扩展类

Dubbo SPI提供@Activate注解用于标注激活扩展类,主要用途在于能通过URL参数模糊匹配到扩展点,然后返回符合条件的激活扩展类。

java 复制代码
public @interface Activate {
    // 激活组
    String[] group() default {};

    // 激活扩展配置, 例如 Activate(value="key1:value1, key2")
    String[] value() default {};
    
    // 排在哪些激活点迁
    @Deprecated
    String[] before() default {};

    // 排在哪些激活点后
    @Deprecated
    String[] after() default {};

    // 顺序
    int order() default 0;
}

3.6.1 激活扩展点配置

java 复制代码
@Activate(group={"mas", "ums"}, value={"key1:value1, key2:value2"})
public class WechatPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing payment via wechat: $" + amount);
    }
}

3.6.2 激活扩展注解匹配机制

激活选项 样例 内容
group {"mas", "ums"} 允许进行匹配的组,为空表示默认匹配,仅支持精确匹配
value {"key1:value1, key2"} 允许进行匹配的参数值,为空默认匹配,支持后缀匹配和精确匹配

接下来,就是细化激活配置与URL参数各种匹配规则。

URL参数 激活配置 匹配机制
?k1=v1&k2=v2 @Activate(value={"k1:v1, k2:v2"}) 精确匹配
?k1=v1&k2=v2 @Activate(value={"k1:v1, k2"}) 参数匹配,URL对应键参数不能为空
?k1=v1&k2=v2 @Activate(value={"k1:v1, g.k2"}) 参数后缀匹配,也就是激活配置参数后缀匹配="." + URL参数Key,URL对应Key参数不能为空

3.7 普通扩展类

普通扩展类无须通过@Activate@Adaptive注解修饰,也不是包装器扩展类,而且必须仅包含无参构造器。

3.8 扩展实例依赖注入

Dubbo SPI可以通过依赖注入增强扩展实例,主要针对就是setter方法。其中,可以额外指定@Inject注解设置注入方式和是否注入。另外,也可以通过@DisableInject禁止当前方法进行依赖注入。

java 复制代码
public @interface Inject {
    // 是否开启注入
    boolean enable() default true;

    // 注入方式
    InjectType type() default ByName;

    enum InjectType{
        ByName,
        ByType
    }
}

3.9 扩展初始化

Dubbo SPI可以Lifecycle生命周期管理接口,方便扩展类实现初始化过程。

csharp 复制代码
public interface Lifecycle {
    // 初始化方法
    void initialize() throws IllegalStateException;

    // 启动方法
    void start() throws IllegalStateException;

    // 销毁方法
    void destroy() throws IllegalStateException;
}

3.10 扩展配置

Dubbo提供META-INF/dubbo/internal/META-INF/dubbo/META-INF/services/三个目录配置扩展,扩展配置文件规范为扩展接口全限定类名,配置文件内容格式为name1,name2,...,=扩展类全限定类名。同时,配置文件支持包装器配置。

例如,配置文件META-INF/dubbo/internal/com.feiyu.PaymentService进行扩展类配置,配置内容如下。

properties 复制代码
credit=com.feiyu.credit.CreditCardPaymentService
pay,pal,paypal=com.feiyu.pay.PayPalPaymentService
com.feiyu.pay.WechatPaymentService
com.feiyu.wrapper.WrapperPaymentService ## 包装类

扩展类名,Dubbo底层支持手动指定 或者框架自动规则解析 两种方式。其中,框架自动规则解析逻辑就是去除扩展接口名转换为小写字母获得,比如com.feiyu.pay.WechatPaymentService去除PaymentService得到wechat

需要注意,每个扩展接口可以配置<扩展名, 扩展类>映射,但是扩展名必须保证扩展接口级别唯一,否则就会抛出异常。

04 Dubbo SPI分析

类似Java SPI ServiceLoader功能,Dubbo SPI底层通过ExtensionLoader封装扩展类加载,以及提供指定参数条件、默认或者激活方式获取扩展类路径,所以ExtensionLoader也是源码重点研究对象。

4.1 ExtensionLoader实例化

ExtensionLoader没有提供公开构造函数,只能通过ExtensionLoader.getExtensionLoader(Class<T> type)获取ExtensionLoader实例。

java 复制代码
public class ExtensionLoader<T> {
    // 服务接口扩展加载器缓存
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
    // 名字分割匹配模式, 格式=n1,n2,...
    private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
    
    // 需要加载服务接口类型
    private final Class<?> type;
    // 扩展加载工厂: 用于自定义实现扩展加载
    private final ExtensionFactory objectFactory;

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        // 使用激活ExtensionFactory加载服务提供者
        if(type != ExtensionFactory.class) {
            objectFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
        }
    }
    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;
    }
    
    private static <T> boolean withExtensionAnnotation(Class<T> type) {
        // 接口有添加@SPI注解
        return type.isAnnotationPresent(SPI.class);
    }
}

public @interface SPI {
    // 指定默认扩展名
    String value() default "";
}

获取ExtensionLoader实例过程可以看到,扩展接口必须是被@SPI注解修饰的接口,扩展接口加载器创建和初始化后缓存到ConcurresntHashMap结构。

4.2 基础解析

4.2.1 默认扩展名解析

方法 功能
ExtensionLoader#cacheDefaultExtensionName( ) 缓存默认扩展名
java 复制代码
public class ExtensionLoader<T> {
    // 扩展接口默认扩展名, 通过@SPI(value=?)指定, 仅支持1个默认扩展名
    private String cachedDefaultName;

    // 缓存默认扩展名
    private void cacheDefaultExtensionName() {
        // 获取扩展接口@SPI注解
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }
        // @SPI(value=?): value=默认扩展名(仅支持1个配置), 格式=n1,n2,...,
        String value = defaultAnnotation.value();
        if ((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 " + type.getName() + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
                cachedDefaultName = names[0];
            }
        }
    }
}

4.2.2 初始化配置加载策略

Dubbo SPI申明<font style="color:#74B602;">ExtensionLoader#loadLoadingStrategies</font>方法读取扩展配置加载策略,底层使用Java SPI机制进行加载策略,然后通过排序后返回。另外,也提供额外方法手动设置加载策略。

java 复制代码
public class ExtensionLoader<T> {
    // 初始化加载策略
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();

    public static void setLoadingStrategies(LoadingStrategy... strategies) {
        if (ArrayUtils.isNotEmpty(strategies)) {
            ExtensionLoader.strategies = strategies;
        }
    }
    
    private static LoadingStrategy[] loadLoadingStrategies() {
        // 通过Java SPI加载LoadingStrategy服务提供者
        return stream(load(LoadingStrategy.class).spliterator(), false)
                .sorted()
                .toArray(LoadingStrategy[]::new);
    }
}

public interface LoadingStrategy extends Prioritized {
    // 扩展目录
    String directory();

    // 优先扩展加载器
    default boolean preferExtensionClassLoader() {
        return false;
    }
    
    // 排除包
    default String[] excludedPackages() {
        return null;
    }

    // 是否覆盖加载
    default boolean overridden() {
        return false;
    }
}

ExtensionLoader提供扩展加载策略支持,底层使用Java SPI扫描和排序这些扩展加载策略,默认实现包括DubboInternalLoadingStrategyDubboLoadingStrategyServicesLoadingStrategy三种实现。

加载策略 加载路径 优先级 是否支持覆盖
DubboInternalLoadingStrategy META-INF/dubbo/internal/ 最高 N
DubboLoadingStrategy META-INF/dubbo/ 中等 Y
ServicesLoadingStrategy META-INF/services/ 最低 Y

4.2.3 获取扩展类

ExtensionLoader提供getExtensionClasses方法获取扩展类,标签缓存起来避免二次加载。

java 复制代码
public class ExtensionLoader<T> {
    // 缓存已加载扩展类, 格式: n1,n2,...=扩展类全限定名
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    
    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;
    }
}

4.2.4 读取扩展配置

ExtensionLoader提供=DubboInternalLoadingStrategyDubboLoadingStrategyServicesLoadingStrategy三种加载扩展策略,分别调用ExtensionLoader#loadDirectory执行不同目录配置扩展类加载。

java 复制代码
public class ExtensionLoader<T> {
    // 初始化加载策略
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
    // 每行解析异常信息
    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
    
    // 读取加载策略加载扩展配置
    private Map<String, Class<?>> loadExtensionClasses() {
        // 解析和缓存默认扩展名
        cacheDefaultExtensionName();

        // 扩展类
        Map<String, Class<?>> extensionClasses = new HashMap<>();

        // 循环扩展策略进行加载
        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;
    }
    
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        loadDirectory(extensionClasses, dir, type, false, false);
    }

    // 读取扩展目录文件加载扩展类
    // @param extensionLoaderClassLoaderFirst: 首先选择ExtensionLoader类加载器
    private void loadDirectory(Map<String, Class<?>> extensionClasses, 
                               String dir, 
                               String type,
                               boolean extensionLoaderClassLoaderFirst, 
                               boolean overridden, String... excludedPackages) {
        // 文件路径, 比如:META-INF/dubbo/com.feiyu.PaymentService
        String fileName = dir + type;
        try {
            Enumeration<URL> urls = null;
            // 获取类加载器
            ClassLoader classLoader = findClassLoader();

            // 通过ExtensionLoader进行加载
            if (extensionLoaderClassLoaderFirst) {
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    // 加载文件资源
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }

            // 如果没有加载到资源, 使用默认类加载器进行加载
            if (urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }

            if (urls != null) {
                while (urls.hasMoreElements()) {
                    // 循环进行资源加载
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
    
    // 读取扩展配置文件加载扩展类
    private void loadResource(Map<String, Class<?>> extensionClasses, 
                              ClassLoader classLoader,
                              java.net.URL resourceURL, 
                              boolean overridden, 
                              String... excludedPackages) {
        try {
            // 读取文件
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                String clazz = null;
                while ((line = reader.readLine()) != null) {
                    // 读取配置, 格式:n1,n2=com.feiyu.credit.CreditCardPaymentService
                    // 截断行注释内容
                    final int ci = line.indexOf('#');
                    if (ci >= 0) {
                        line = line.substring(0, ci);
                    }
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                clazz = line.substring(i + 1).trim();
                            } else {
                                clazz = line;
                            }
                            if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
                                loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
}

4.3 解析扩展配置

4.3.1 解析扩展类

实现扩展类分类解析,比如自适应扩展类、包装器扩展类、激活扩展类和普通扩展类,然后缓存到对应位置方便查找。

java 复制代码
public class ExtensionLoader<T> {
    // 初始化加载策略
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
    // 每行解析异常信息
    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
    // 扩展类名字, 仅缓存第一个配置扩展类名
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    
    // 加载约定扩展类
    private void loadClass(Map<String, Class<?>> extensionClasses, 
                           java.net.URL resourceURL, 
                           Class<?> clazz, 
                           String name,
                           boolean overridden) throws NoSuchMethodException {
        // 扩展类必须实现扩展接口
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }
        // 自适应扩展类: 如果扩展类有被@Adaptive注解
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
        } 
        else if (isWrapperClass(clazz)) {
            // 扩展类是包装类型
            cacheWrapperClass(clazz);
        } else {
            // 获取无参构造器
            clazz.getConstructor();
            
            // 没有配置名字的扩展器
            if (StringUtils.isEmpty(name)) {
                // 获取扩展类名
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            
            // 已配置扩展名字
            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 缓存接活扩展类
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }
    // 是否包装类
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }
    private String findAnnotationName(Class<?> clazz) {
        // 获取扩展器Extension注解
        org.apache.dubbo.common.Extension extension = clazz.getAnnotation(org.apache.dubbo.common.Extension.class);
        if (extension != null) {
            return extension.value();
        }
        // 旧逻辑: 通过类名获取, 截取扩展接口后缀即为扩展类名
        String name = clazz.getSimpleName();
        if (name.endsWith(type.getSimpleName())) {
            name = name.substring(0, name.length() - type.getSimpleName().length());
        }
        return name.toLowerCase();
    }
    private void cacheName(Class<?> clazz, String name) {
        // 只缓存第一个扩展类名
        if (!cachedNames.containsKey(clazz)) {
            cachedNames.put(clazz, name);
        }
    }
    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, 
                                      Class<?> clazz, 
                                      String name, 
                                      boolean overridden) {
        // 扩展类名 => 扩展类
        Class<?> c = extensionClasses.get(name);
        // 没有解析或者可以覆盖
        if (c == null || overridden) {
            extensionClasses.put(name, clazz);
        } 
        // 扩展名冲突, 抛出异常
        else if (c != clazz) {
            unacceptableExceptions.add(name);
            String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
            logger.error(duplicateMsg);
            throw new IllegalStateException(duplicateMsg);
        }
    }
}

4.3.2 解析自适应扩展类

Dubbo SPI提供@Adaptive注解注释自适应扩展类,其中,META-INF/dubbo/internal/扩展类不支持覆盖配置。

java 复制代码
public class ExtensionLoader<T> {
    // 自适应扩展类
    private volatile Class<?> cachedAdaptiveClass;
    
    private void cacheAdaptiveClass(Class<?> clazz, boolean overridden) {
        if (cachedAdaptiveClass == null || overridden) {
            cachedAdaptiveClass = clazz;
        } 
        // 仅支持一次设置
        else if (!cachedAdaptiveClass.equals(clazz)) {
            throw new IllegalStateException("More than 1 adaptive class found: "
                    + cachedAdaptiveClass.getName() + ", " + clazz.getName());
        }
    }
}

4.3.3 解析包装器扩展类

java 复制代码
public class ExtensionLoader<T> {
    // 包装类扩展类
    private Set<Class<?>> cachedWrapperClasses;
    
    private void cacheWrapperClass(Class<?> clazz) {
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }
}

4.3.4 解析激活扩展类

Dubbo SPI提供<font style="color:#74B602;">@Activate</font>注解用于标注激活扩展类,通过cachedActivates属性缓存注解值。

typescript 复制代码
public class ExtensionLoader<T> {
    // 激活扩展点: 扩展类标注@Activate, 如果存在多个扩展名只会缓存第一个扩展名作为激活点
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    
    private void cacheActivateClass(Class<?> clazz, String name) {
        // 获取扩展类@Activate注解
        Activate activate = clazz.getAnnotation(Activate.class);
        // 缓存激活注解
        if (activate != null) {
            cachedActivates.put(name, activate);
        } else {
            // support com.alibaba.dubbo.common.extension.Activate
            com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
            if (oldActivate != null) {
                cachedActivates.put(name, oldActivate);
            }
        }
    }
}

4.4 创建实例

Dubbo提供ExtensionLoader#createExtension创建扩展实例,通过ExtensionLoader#injectExtension进行实例增强(setter),然后进行包装器类增强处理,最后进行扩展类实例初始化。

java 复制代码
public class ExtensionLoader<T> {
    // 缓存扩展点实例
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    // 缓存扩展名原始实例
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
    // 异常扩展名
    private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
    
    // 创建扩展实例
    private T createExtension(String name, boolean wrap) {
        // 获取扩展类
        Class<?> clazz = getExtensionClasses().get(name);
        // 没有找到直接抛出异常
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            // 获取扩展名原始实例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                // 创建并缓存扩展类原始实例
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            
            // 扩展实例依赖注入增强处理
            injectExtension(instance);

            // 保证处理
            if (wrap) {
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                // 包装类排序
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        // 获取包装器扩展类@Wrapper注解
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null 
                            // 包含名称 以及 不在排除名称之外
                            || (ArrayUtils.contains(wrapper.matches(), name) 
                                && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            // 创建包装扩展实例, 然后进行依赖注入
                            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);
        }
    }
}

4.4.1 扩展实例依赖注入

java 复制代码
public class ExtensionLoader<T> {
    // 扩展工厂
    private final ExtensionFactory objectFactory;
    
    // 扩展实例依赖注入
    private T injectExtension(T instance) {
        if (objectFactory == null) {
            return instance;
        }

        try {
            // 循环所有方法
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {
                    continue;
                }
                
                // 方法标注@DisableInject注解, 不进行依赖注入
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }

                // 获取依赖注入类型参数
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }
                
                // 获取方法名
                String property = getSetterProperty(method);
                
                // 获取方法@Inject注解
                Inject inject = method.getAnnotation(Inject.class);
                if (inject == null) {
                    injectValue(instance, method, pt, property);
                } else {
                    // 禁止依赖注入
                    if (!inject.enable()) {
                        continue;
                    }

                    if (inject.type() == Inject.InjectType.ByType) {
                        injectValue(instance, method, pt, null);
                    } else {
                        injectValue(instance, method, pt, property);
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
    
    // 获取方法名
    private String getSetterProperty(Method method) {
        return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
    }

    private void injectValue(T instance, Method method, Class<?> pt, String property) {
        try {
            // 获取依赖注入值
            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);
        }
    }
}

4.4.2 初始化扩展实例

java 复制代码
public class ExtensionLoader<T> {
    // 扩展工厂
    private final ExtensionFactory objectFactory;
    
    // 初始化扩展实例
    private void initExtension(T instance) {
        // 如果扩展实现Lifecycle, 调用initialize进行初始化
        if (instance instanceof Lifecycle) {
            Lifecycle lifecycle = (Lifecycle) instance;
            lifecycle.initialize();
        }
    }
}

4.5 获取扩展点

4.5.1 获取指定名称扩展点

Dubbo SPI重载getExtension方法对外获取指定名称扩展类,其中,底层使用Holder持有当前扩展点实例,不过可以直接设计缓存实例对象。

java 复制代码
public class ExtensionLoader<T> {
    // 缓存扩展点实例
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    // 缓存扩展名原始实例
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
    // 异常扩展名
    private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
    
    public T getExtension(String name) {
        return getExtension(name, true);
    }

    public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 获取或者创建持有Holder
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 创建和缓存扩展
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    
    private Holder<Object> getOrCreateHolder(String name) {
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        return holder;
    }
}

4.5.2 获取默认扩展点

java 复制代码
public class ExtensionLoader<T> {
    // 扩展接口默认扩展名, 通过@SPI(value=?)指定, 仅支持1个默认扩展名
    private String cachedDefaultName;
    
    public T getDefaultExtension() {
        // 加载扩展类
        getExtensionClasses();
        // 缓存默认扩展名不能为空或者true
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        
        // 指定名称获取扩展: @SPI(value=?)注解值就是默认扩展名
        return getExtension(cachedDefaultName);
    }
}

4.5.3 获取自适应扩展点

java 复制代码
public class ExtensionLoader<T> {
    // 自适应实例缓存: 每个扩展接口仅支持1个自适应扩展点
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    // 自适应扩展创建异常
    private volatile Throwable createAdaptiveInstanceError;
    
    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;
    }
    
    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);
        }
    }
}

4.5.4 获取激活扩展点

Dubbo底层提供4个getActivateExtension重载方法,方便用户根据URL获取激活扩展点。获取激活扩展点实例,需要匹配激活类匹配,以及常规扩展点。

匹配类型 匹配规则
激活扩展点 激活扩展点解析逻辑,就是循环激活扩展点与URL参数进行匹配,如果匹配就添加到激活扩展集合。 激活扩展点匹配逻辑,包括是否允许激活扩展点匹配和激活扩展匹配。 1. 是否进行激活扩展点匹配:获取扩展名不能包含"-default" 2. 激活扩展点匹配 (1)激活扩展点注解匹配:@Activate(group={}, value={}) (2)激活扩展名匹配:参数扩展名 not_exsited (激活扩展名, -激活扩展名)
常规扩展点 常规扩展点解析逻辑:循环传入参数扩展名进行匹配,如果解析到扩展名=default就添加到已解析扩展到最前面,否则都追加到后面。 扩展名是否匹配:参数扩展名不是以去除符号(-)为前缀,以及扩展名不存在去除参数,如values={"timeout", "-timeout"}
java 复制代码
public class ExtensionLoader<T> {
    // 激活扩展点: 扩展类标注@Activate, 如果存在多个扩展名只会缓存第一个扩展名作为激活点
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    
    public List<T> getActivateExtension(URL url, String key) {
        return getActivateExtension(url, key, null);
    }
    
    public List<T> getActivateExtension(URL url, String[] values) {
        return getActivateExtension(url, values, null);
    }
    
    public List<T> getActivateExtension(URL url, String key, String group) {
        // 获取URL参数key=?对应值, 也就是扩展点名称
        String value = url.getParameter(key);
        return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
    }

    // @param values=扩展名称集合, group=扩展组
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> activateExtensions = new ArrayList<>();
        // 创建TreeMap: 也就是红黑树排序
        TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
        
        Set<String> loadedNames = new HashSet<>();
        Set<String> names = CollectionUtils.ofSet(values);

        // 激活扩展点匹配: 扩展点不包含"-default"值, 也就是去除(-)默认配置
        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;
                
                // apache注解
                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;
                }
                // 1. paramGroup in (activateGroup)
                // 2. activateName not_in (paramNames)
                // 3. -activateName not_in (paramNames)
                // 4. @Activate.value match url.params
                // 5. 不重复加载
                if (isMatchGroup(group, activateGroup)
                        && !names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        && isActive(activateValue, url)
                        && !loadedNames.contains(name)) {
                    // 缓存扩展类与扩展实例映射
                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                    loadedNames.add(name);
                }
            }
            if (!activateExtensionsMap.isEmpty()) {
                activateExtensions.addAll(activateExtensionsMap.values());
            }
        }
        
        List<T> loadedExtensions = new ArrayList<>();
        // 扩展点匹配
        for (String name : names) {
            // -: 表示不匹配
            if (!name.startsWith(REMOVE_VALUE_PREFIX)
                    && !names.contains(REMOVE_VALUE_PREFIX + name)) {
                // 控制不进行重复加载
                if (!loadedNames.contains(name)) {
                    // DEFAULT_KEY = default
                    if (DEFAULT_KEY.equals(name)) {
                        // 将扩展点添加到激活扩展点之前
                        if (!loadedExtensions.isEmpty()) {
                            activateExtensions.addAll(0, loadedExtensions);
                            loadedExtensions.clear();
                        }
                    } else {
                        // 缓存扩展点实例
                        loadedExtensions.add(getExtension(name));
                    }
                    loadedNames.add(name);
                } else {
                    // 重复加载, warn提示
                    String simpleName = getExtensionClass(name).getSimpleName();
                    logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name + ". Ignored Class Name: " + simpleName);
                }
            }
        }
        // 末尾添加到激活扩展点
        if (!loadedExtensions.isEmpty()) {
            activateExtensions.addAll(loadedExtensions);
        }
        return activateExtensions;
    }

    // 组匹配
    private boolean isMatchGroup(String group, String[] groups) {
        if (StringUtils.isEmpty(group)) {
            return true;
        }
        // 找到相同组即可
        if (groups != null && groups.length > 0) {
            for (String g : groups) {
                if (group.equals(g)) {
                    return true;
                }
            }
        }
        return false;
    }

    // 激活点匹配
    // 例如:@Active(value="key1:value1, key2:value2"), URL参数能模糊匹配参数即可
    private boolean isActive(String[] keys, URL url) {
        if (keys.length == 0) {
            return true;
        }
        for (String key : keys) {
            // @Active(value="key1:value1, key2:value2")
            String keyValue = null;
            if (key.contains(":")) {
                String[] arr = key.split(":");
                key = arr[0];
                keyValue = arr[1];
            }

            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                     && ((keyValue != null && keyValue.equals(v)) 
                         || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
                    return true;
                }
            }
        }
        return false;
    }
}

4.6 手动添加扩展点

java 复制代码
public class ExtensionLoader<T> {
    // 缓存已加载扩展类, 格式: n1,n2,...=扩展类全限定名
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    // 扩展点对应扩展名称, 如果配置存在多个名称只会第一个解析值
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();

    
    public void addExtension(String name, Class<?> clazz) {
        // 先执行扫描添加扩展
        getExtensionClasses();

        // 扩展类必须经常扩展接口
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Input type " + clazz + " doesn't implement the Extension " + type);
        }
        // 扩展类不能是接口
        if (clazz.isInterface()) {
            throw new IllegalStateException("Input type " + clazz + " can't be interface!");
        }
        // 非自适应扩展
        if (!clazz.isAnnotationPresent(Adaptive.class)) {
            if (StringUtils.isBlank(name)) {
                throw new IllegalStateException("Extension name is blank (Extension " + type + ")!");
            }
            if (cachedClasses.get().containsKey(name)) {
                throw new IllegalStateException("Extension name " + name + " already exists (Extension " + type + ")!");
            }
            
            // 缓存扩展类和扩展名映射关系
            cachedNames.put(clazz, name);
            cachedClasses.get().put(name, clazz);
        } else {
            // 不能重复添加自适应扩展
            if (cachedAdaptiveClass != null) {
                throw new IllegalStateException("Adaptive Extension already exists (Extension " + type + ")!");
            }
            cachedAdaptiveClass = clazz;
        }
    }
}

4.7 其他

4.7.1 是否包含扩展

java 复制代码
public class ExtensionLoader<T> {
    public boolean hasExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        // 获取扩展类
        Class<?> c = this.getExtensionClass(name);
        return c != null;
    }
}

4.7.2 获取扩展类和名称

java 复制代码
public class ExtensionLoader<T> {
    // 扩展点对应扩展名称, 如果配置存在多个名称只会第一个解析值
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();

    // 获取扩展实例对应扩展点名称
    public String getExtensionName(T extensionInstance) {
        return getExtensionName(extensionInstance.getClass());
    }

    // 获取扩展类对应扩展点名称
    public String getExtensionName(Class<?> extensionClass) {
        getExtensionClasses();// load class
        return cachedNames.get(extensionClass);
    }

    // 获取默认扩展名
    public String getDefaultExtensionName() {
        getExtensionClasses();
        return cachedDefaultName;
    }

    // 获取扩展类
    private Class<?> getExtensionClass(String name) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (name == null) {
            throw new IllegalArgumentException("Extension name == null");
        }
        return getExtensionClasses().get(name);
    }
}

4.7.3 获取已加载扩展点

java 复制代码
public class ExtensionLoader<T> {
    // 缓存扩展点实例
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    
    // 获取已加载扩展实例
    public T getLoadedExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        Holder<Object> holder = getOrCreateHolder(name);
        return (T) holder.get();
    }

    private Holder<Object> getOrCreateHolder(String name) {
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        return holder;
    }
    
    public Set<String> getLoadedExtensions() {
        return Collections.unmodifiableSet(new TreeSet<>(cachedInstances.keySet()));
    }

    public List<T> getLoadedExtensionInstances() {
        List<T> instances = new ArrayList<>();
        cachedInstances.values().forEach(holder -> instances.add((T) holder.get()));
        return instances;
    }
}

4.7.4 获取扩展点原始实例

java 复制代码
public class ExtensionLoader<T> {
    // 缓存扩展名原始实例
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
 
    public T getOriginalInstance(String name) {
        getExtension(name);
        Class<?> clazz = getExtensionClasses().get(name);
        return (T) EXTENSION_INSTANCES.get(clazz);
    }
}

4.7.5 获取支持扩展

java 复制代码
public class ExtensionLoader<T> {
    // 获取支持扩展名
    public Set<String> getSupportedExtensions() {
        // 获取扩展类
        Map<String, Class<?>> clazzes = getExtensionClasses();
        return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
    }

    // 获取支持扩展实例
    public Set<T> getSupportedExtensionInstances() {
        List<T> instances = new LinkedList<>();
        Set<String> supportedExtensions = getSupportedExtensions();
        if (CollectionUtils.isNotEmpty(supportedExtensions)) {
            for (String name : supportedExtensions) {
                instances.add(getExtension(name));
            }
        }
        // 排序后返回
        sort(instances, Prioritized.COMPARATOR);
        return new LinkedHashSet<>(instances);
    }
}

05 总结

SPI作为提供良好的扩展和可插拔实现,插件式微内核架构可以重要考虑的设计方案。Dubbo SPI机制比较灵活,提供默认、自适应、激活和普通扩展点支持,相比Java SPI更加灵活多变。

相关推荐
Piper蛋窝4 小时前
深入 Go 语言垃圾回收:从原理到内建类型 Slice、Map 的陷阱以及为何需要 strings.Builder
后端·go
六毛的毛7 小时前
Springboot开发常见注解一览
java·spring boot·后端
AntBlack7 小时前
拖了五个月 ,不当韭菜体验版算是正式发布了
前端·后端·python
31535669137 小时前
一个简单的脚本,让pdf开启夜间模式
前端·后端
uzong7 小时前
curl案例讲解
后端
一只叫煤球的猫8 小时前
真实事故复盘:Redis分布式锁居然失效了?公司十年老程序员踩的坑
java·redis·后端
大鸡腿同学9 小时前
身弱武修法:玄之又玄,奇妙之门
后端
轻语呢喃11 小时前
JavaScript :字符串模板——优雅编程的基石
前端·javascript·后端
MikeWe11 小时前
Paddle张量操作全解析:从基础创建到高级应用
后端
岫珩11 小时前
Ubuntu系统关闭防火墙的正确方式
后端