IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SPI 自适应拓展 -> 正文阅读

[Java知识库]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()方法感兴趣可以自己看下.
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:09:53  更:2022-04-09 18:11:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:32:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码