SPI 自适应拓展

dubbo spi详解

dubbo的spi详解
前面大概讲了一下dubbo的spi机制.其中讲到注入的时候,在Car上加了个注解
@Adaptive(value = “carType”),讲到会通过url里面的参数,key为carType,根据value来获取对应的car.这里面就要讲到Adaptive 注解 - 自适应扩展;

自适应扩展

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。

  • 自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。扩展的接口生成一个代理类,可以通过JDK 或者 javassist 编译你生成的代理类代码,然后通过反射创建实例。
    这个生成的代理类代码里面的实现会根据本来方法的请求参数得知需要的扩展类,然后通过 ExtensionLoader.getExtensionLoader(type.class).getExtension(从参数得来的name),来获取真正的实例来调用。

  • 当然也可以就是自己创建个adapter类,来手动实现自适应扩展.在类上加个@Adaptive注解就好了

说简单点就是我想根据配置来进行 SPI 扩展的加载,但是我不想在启动的时候让扩展被加载,我想根据请求时候的参数来动态选择对应的扩展。

demo

新建个Bus类,然后同Car类一样建两个实现,再建一个BusRouter类作为参数路由类,然后在创建一个BusAdapter类作为自适应类,并配置到配置文件中:

public class BusRouter {

    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}



@SPI
public interface Bus {

    @Adaptive
    void getColor(BusRouter busRouter);

}

public class BlackBus implements Bus {

    @Override
    public void getColor(BusRouter busRouter) {
        System.out.println("black");
    }

}

public class RedBus implements Bus {
    @Override
    public void getColor(BusRouter busRouter) {
        System.out.println("red");
    }
}

@Adaptive
public class BusAdapter implements Bus {

    @Override
    public void getColor(BusRouter busRouter) {
        ExtensionLoader<Bus> extensionLoader =
                ExtensionLoader.getExtensionLoader(Bus.class);
        Bus red = extensionLoader.getExtension(busRouter.getName());
        red.getColor(busRouter);
    }

}

在META-INF.services下建立文件org.example.api:

black=org.example.impl.BlackBus
red=org.example.impl.RedBus
org.example.BusAdapter

测试类:

  @Test
    public void driverBus1() throws Exception {
        ExtensionLoader<Bus> extensionLoader =
                ExtensionLoader.getExtensionLoader(Bus.class);
        Bus trucker = extensionLoader.getAdaptiveExtension();
        BusRouter busRouter = new BusRouter();
        busRouter.setName("red");
        trucker.getColor(busRouter);
    }

在这里插入图片描述

源码分析

在构建ExtensionLoader的时候,会初始化ExtensionFactory,
在这里插入图片描述
getAdaptiveExtension方法会直接返回带有@Adaptive标注的类,而cachedAdaptiveInstance在创建ExtensionLoader的时候就加载了

  @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                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);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

在这里插入图片描述

有@Adaptive标注的类的实现

前面讲ExtensionFactory的时候就说了两个实现.实际是有三个实现;
在这里插入图片描述
实际初始化的是AdaptiveExtensionFactory这个类,AdaptiveExtensionFactory是少有被标注的类,还有一个是AdaptiveCompiler类.AdaptiveExtensionFactory类的源码比较简单,就是获取到对应实现,然后getExtension遍历获取对应的类型类,ExtensionFactory其实就dubbo spi的自适应的实现:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

如果没有@Adaptive标注的类

没有cachedAdaptiveClass类则会通过createAdaptiveExtension->getAdaptiveExtensionClass->createAdaptiveExtensionClass方法进行创建:
在这里插入图片描述

  @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() {
        // 通过 SPI 获取所有的拓展类
        getExtensionClasses();
        // 检查缓存,若缓存不为空,则直接返回缓存
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 创建自适应拓展类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

    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();
        // 编译代码,生成 Class
        return compiler.compile(code, classLoader);
    }

比如前面的测试类bus,通过AdaptiveClassCodeGenerator生成的code如下:
在这里插入图片描述

package org.example.api;

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

public class Car$Adaptive implements org.example.api.Car {
    public void getColor(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("carType");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.example.api.Car) name from url (" + url.toString() + ") use keys([carType])");
        org.example.api.Car extension = (org.example.api.Car) ExtensionLoader.getExtensionLoader(org.example.api.Car.class).getExtension(extName);
        extension.getColor(arg0);
    }
}

可以看到会生成包,也会生成 import 语句,类名就是接口加个$Adaptive,并且实现这接口,如果没有标记 Adaptive 注解的方法调用的话直接抛错。内部生成逻辑也比较简单就是通过url.getParameter(“carType”)方法来获取对应的扩展点名称,然后再通过ExtensionLoader.getExtensionLoader(org.example.api.Car.class).getExtension(extName);来获取对应的扩展类,执行对应方法.
生成类generate()方法感兴趣可以自己看下.
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值