深入DubboSPI原理

版本:2.7.5

目录

1.Dubbo SPI

2.Dubbo Wrapper

3.@SPI @Adaptive @Activate

4.Dubbo SPI核心流程源码分析


1.Dubbo SPI

在看Dubbo的SPI之前,先来看看Java的SPI

package com.mingzhi;

public interface Car {
    void printCarName();
}

定义一个接口,然后创建如下图的文件,文件名为接口的全包名,内容为接口实现类的全包名

随后main方法中就可以调用ServiceLoader.load去加载实现类

package com.mingzhi;

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) {
        ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
        for (Car car : serviceLoader) {
            car.printCarName();
        }
    }
}

但是这里有一定的局限性,最直接的就是会拿到所有的实现类去实例化,并且如果实现类中还有其他依赖配置,也不能自动注入,于是Dubbo的SPI就在Java SPI的基础上完善了许多。

先来看看Dubbo的SPI基本实现(先下载并导入dubbo源码到ide中)

在META-INF中新建dubbo文件夹

里面内容有一点变化,用=连接了key和value,value任然是实现类的全包名

red=com.mingzhi.RedCar
black=com.mingzhi.BlackCar

接着就能够通过dubbo的SPI获取到特定key的实现类

ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("black");
System.out.println(car.getCarName());

2.Dubbo Wrapper

dubbo的wrapper能够在执行接口方法之前或者之后添加逻辑,达到类似AOP的功能

首先先定义某个接口的wrapper类,同时也需要实现这个接口

最重要的一点是需要一个此接口的成员变量和构造函数,这样dubbo就将识别此类为wrapper,然后就实现了类似AOP的功能

public class CarWrapper implements Car {

    private Car car;

    public CarWrapper(Car car) {
        this.car = car;
    }

    @Override
    public String getCarName() {
        System.out.println("wrapper start");
        System.out.println("before");
        System.out.println(car.getCarName());
        System.out.println("after");
        return "wrapper end";
    }
}

 打印结果符合预期

3.@SPI @Adaptive @Activate

@SPI注解使用在接口上

可以指定value,如果指定了value则表明:如果没有指定具体的实现类(->没有在某个实现类上标上注解@Adaptive)则extensionLoader.getAdaptiveExtension()会使用value的实现类,如果有某个实现类上标上注解@Adaptive,则不管@SPI是否有值都会去加载@Adaptive的实现类

@Adaptive注解使用在实现类或者接口方法上

1.使用在实现类上则表明此实现类为接口的默认实现类,优先级最高

2.使用在接口方法上,则会在初始化的时候生成一个动态代理类,被标记的方法会实现为根据传入的Url参数来执行getExtensionLoader(xx.class).getExtension(name)找到最终执行的那个实现类

对比:类级别省略了生成动态代理类的过程,由指定类决定具体实现,另外对于同一个扩展点,类级别的Adaptive只能有一个

@Activate注解待更新

4.Dubbo SPI核心流程源码分析

首先看dubbo中SPI最常见的获取实现类的方式

ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);

进入getExtensionLoader后,可以看到就是去拿接口所对应的extensionLoader,这里可以体现出来一个接口只会有一个扩展加载器

 缓存扩展加载器的结构是ConcurrentMap,key是接口类型,value是扩展加载器

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

然后看具体去拿扩展实现类的这句代码

 Car car = extensionLoader.getExtension("black");

 进入getExtension方法后可以看到显示一些判断,如果传进来的为true则去获取默认扩展类

// 获取默认扩展类
if ("true".equals(name)) {
    return getDefaultExtension();
}

一直走到org.apache.dubbo.common.extension.ExtensionLoader#cacheDefaultExtensionName去获取默认配置类

 后面会去配置文件中读取扩展类

 后面就会getExtension去加载配置的默认实现类

如果不为true则会进入下面的逻辑

org.apache.dubbo.common.extension.ExtensionLoader#createExtension

首先会去加载dubbo配置文件中的内容

 先看getExtensionClasses方法,会从缓存中先拿,如果没有缓存则去loadExtensionClasses后放入缓存中

    /**
     * 加载当前ExtensionLoader对象中指定的接口的所有扩展
     * @return
     */
    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses(); // 加载、解析文件 Map
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

接着会根据拿到的配置中的类型实例出对象然后放入缓存中 

创建实例后会进行依赖注入

// 依赖注入 IOC
injectExtension(instance);

里面会从2个地方(Dubbo SPI机制所产生,也可能是从Spring容器中所获得的对象)获取对象后再反射调用setter进行注入

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

依赖注入ioc之后会进行dubbo的aop也就是包装一层wrapper

 首先会先去拿到wrapper的缓存Set(无序所以在有多个wrapper的时候返回的不一定),这个Set是在解析配置文件的时候添加进来的(根据配置文件中类是否有构造方法来判断)

 判断是否为wrapper class

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值