学习ShardingSphere前置知识

学习ShardingSphere前置准备知识

一. SPI

SPI(Service Provider Interface)是一种Java的扩展机制,用于实现组件之间的松耦合。在SPI模型中,服务提供者(Service Provider)定义了一组接口,而服务的使用者可以根据接口编写代码。服务提供者则提供了接口的具体实现,这些实现可以被动态加载和替换。

下面是对SPI的理解:

1.接口定义: SPI的核心是一个接口,定义了一组规范或协议,以描述一种服务。以java.sql.Driver为例

2.服务提供者: 服务提供者是实现了接口的具体类。这些类通常通过在类路径中的META-INF/services目录下提供一个配置文件来注册自己。
在这里插入图片描述

其中在配置文件中的全类名为服务提供者,它实现了定义的接口
在这里插入图片描述

3.服务加载: 使用者通过查找和加载在类路径中的META-INF/services目录下的配置文件,获取服务提供者的实现类。Java的ServiceLoader类通常用于加载这些服务提供者。
使用ServiceLoader.load()进行服务发现与对应的配置文件的类进行加载
4.动态替换: 由于服务提供者的实现是通过配置文件注册的,因此可以在不修改使用者代码的情况下动态替换服务提供者。这使得系统更加灵活,并且可以在运行时适应不同的实现。

5.松耦合: SPI实现了松耦合的设计,允许开发者通过接口定义和服务提供者实现分离。这样,服务提供者可以独立于使用者进行开发、测试和部署。


二. ServiceLoader.load()

public final class ServiceLoader<S>
    implements Iterable<S>
{
   @CallerSensitive
   public static <S> ServiceLoader<S> load(Class<S> service) {
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
   }
    // 实现Iterable接口必须要重写的方法 主要通过spi发现服务提供者,并进行驱动注册,加载
   public Iterator<S> iterator() {
      // create lookup iterator if needed
      if (lookupIterator1 == null) {
         lookupIterator1 = newLookupIterator();
      }

      return new Iterator<S>() {

         // record reload count
         final int expectedReloadCount = ServiceLoader.this.reloadCount;

         // index into the cached providers list
         int index;

         /**
          * Throws ConcurrentModificationException if the list of cached
          * providers has been cleared by reload.
          */
         private void checkReloadCount() {
            if (ServiceLoader.this.reloadCount != expectedReloadCount)
               throw new ConcurrentModificationException();
         }

         @Override
         public boolean hasNext() {
            checkReloadCount();
            if (index < instantiatedProviders.size())
               return true;
            return lookupIterator1.hasNext();
         }

         @Override
         public S next() {
            checkReloadCount();
            S next;
            if (index < instantiatedProviders.size()) {
               next = instantiatedProviders.get(index);
            } else {
               next = lookupIterator1.next().get();
               instantiatedProviders.add(next);
            }
            index++;
            return next;
         }

      };
   }
}

它实现了Iterable接口,可以增强for循环,在遍历的时候进行相应的处理,接下来先熟悉下Iterable接口的使用

2. Iterable
public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        Iterator var2 = this.iterator();

        while(var2.hasNext()) {
            Object var3 = var2.next();
            var1.accept(var3);
        }

    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
    }
}

其中default方法为默认方法,使用default关键字为接口添加默认方法是为了在不破坏现有实现的情况下,为接口添加新的方法。
不需要强制实现

以下为是实现Iterable接口的例子,必须重写iterator()方法,类表明它可以被迭代(iterable),即可以被用于增强型 for 循环。

public class TestIterable<T> implements  Iterable<T>{

    private List<T> list;

    public TestIterable(List<T> list){
        this.list = list;
    }

    @Override
    public Iterator<T> iterator() {
        return  list.iterator();
    }
}

下面分析下ServiceLoader.load入口出,在java.sql.DriverManager类内部调用,如下代码

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();

try {
    // 此处调用Iterator实现的hasNext方法,查找spi接口
   while (driversIterator.hasNext()) {
       driversIterator.next();
   }
} catch (Throwable t) {
   // Do nothing
}
return null;
}

以上代码通过对Iterator的实现,查找到对应路径下的文件,该路径即是spi的配置文件,文件里配置的是定义接口的实现类,
然后对该实现类进行类加载

        /**
         * Loads and returns the next provider class.
         */
        private Class<?> nextProviderClass() {
            if (configs == null) {
                try {
                    //static final String PREFIX = "META-INF/services/";
                    String fullName = PREFIX + service.getName();
                    if (loader == null) {
                        configs = ClassLoader.getSystemResources(fullName);
                    } else if (loader == ClassLoaders.platformClassLoader()) {
                        // The platform classloader doesn't have a class path,
                        // but the boot loader might.
                        if (BootLoader.hasClassPath()) {
                            configs = BootLoader.findResources(fullName);
                        } else {
                            configs = Collections.emptyEnumeration();
                        }
                    } else {
                        configs = loader.getResources(fullName);
                    }
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return null;
                }
                pending = parse(configs.nextElement());
            }
            String cn = pending.next();
            try {
                return Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service, "Provider " + cn + " not found");
                return null;
            }
        }

ServiceLoader.load静态方法是 Java 提供的一种用于加载服务提供者实现的机制。
它是 Java SPI(Service Provider Interface)的一部分,用于在运行时动态加载服务接口的实现类。
具体来说,ServiceLoader.load 的作用是:

加载服务接口的实现类:通过 ServiceLoader.load(ServiceType.class),可以加载与指定服务接口 ServiceType 相关的所有实现类。这些实现类必须位于 META-INF/services/ 目录下的以服务接口全限定名为名称的配置文件中。

返回一个 ServiceLoader 对象:ServiceLoader.load 返回一个 ServiceLoader 对象,通过这个对象,你可以遍历加载到的所有服务提供者的实例。

public class TestSpi {
    public static void main(String[] args) {
        ServiceLoader<HumanTestSPI> loadHumanTestSPI = ServiceLoader.load(HumanTestSPI.class);
        //遍历加载到的所有服务提供者的实例。
        for (HumanTestSPI humanTestSPI:loadHumanTestSPI){
            humanTestSPI.speak();
        }

    }
}

在这里插入图片描述

以上为学习ShardingSphere前置知识,后面会用到大量的spi接口.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/218827.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python基础之Pip使用全攻略

文章目录 1\. 引言Python的包管理器的重要性为什么需要了解和使用Pip 2\. Pip的基本概念什么是PipPip的历史和发展Pip与其他Python包管理工具的比较 3\. Pip的安装和配置在不同操作系统上安装Pip的方法Pip版本的检查和升级Pip的基础配置 4\. 国内多个镜像源及使用方法常用的国内…

boost::throw_exception错误:修改VS代码生成异常选项为/EHsc

VS2013添加boost头文件和库文件路径后&#xff0c;代码编译报错&#xff1a; 错误 LNK2019 无法解析的外部符号 “void __cdecl boost::throw_exception(class std::exception const &)” (?throw_exceptionboostYAXAEBVexceptionstdZ)&#xff0c;该符号在函数 “public:…

开源项目CuteSqlite开发笔记(二):SQLite的架构

在开发CuteSqlite图形客户端的时候&#xff0c;需要用到SQL的语法解释&#xff0c;来对SQL语句进行优化。找了很多的SQL语法解释器&#xff0c;都不是十分满意&#xff0c;只有翻开Sqlite的源码&#xff0c;看看SQLite对SQL语句的解释过程&#xff0c;本文是翻译的官方文档。 官…

010 数据结构_红黑树

前言 本文将会向你介绍红黑树的概念、性质&#xff0c;以及如何手撕红黑树 1 文章重点 文本首先引入红黑树的概念和性质&#xff0c;性质非常重要对于后面的插入操作来说&#xff0c;文章的核心放在了插入部分&#xff0c;另外看插入部分之前记得看声名和节点的定义哦~ 2 引…

如何让销售技巧培训更有效、更落地

如何让销售技巧培训更有效、更落地 如今各行各业都面临着产品过剩&#xff0c;产品不好卖&#xff01;如何更好的把产品卖出去&#xff0c;更好的创造业绩。除了产品本身要符合市场需要&#xff0c;产品性价比高外&#xff0c;销售人员的销售技巧就非常重要。优秀的销售人员能…

流媒体方案之FFmpeg——实现物联网视频监控项目

目录 前言 一、FFmpeg介绍 二、FFmpeg简易理解 三、FFmpeg的重要概念 四、软硬件准备 五、移植、运行FFmpeg 六、运行FFmpeg 前言 最近想做一个安防相关的项目&#xff0c;所以跟着韦东山老师的视频来学习视频监控方案的相关知识&#xff0c;韦东山老师讲的课非常好&…

数据分享 I 2022年淘宝村名单数据,shp/excel格式数据,共7700多条数据,已可视化处理

今天分享2022年淘宝村名单数据&#xff0c;shp/excel格式数据&#xff0c;共7700多条数据&#xff01; 淘宝村认定条件为电商年销售额达1000万元及以上的行政村(不含居委会) &#xff1b;村活跃网店数达100个及以上&#xff0c;或活跃网店数达家庭户数10%及以上&#xff0c;就…

C++ day52 最长递增子序列 最长连续递增子序列 最长重复子数组

题目1&#xff1a;300 最长递增子序列 题目链接&#xff1a;最长递增子序列 对题目的理解 找出整数数组中最长严格递增子序列的长度 动态规划 动规五部曲 1&#xff09;dp数组及下标i的含义 dp[i]&#xff1a;以nums[i]为结尾的最长递增子序列的长度 递增比较的时候&am…

初探Maven

文章目录 一、Maven概述二、安装配置Maven&#xff08;一&#xff09;下载Maven&#xff08;二&#xff09;安装Maven&#xff08;三&#xff09;配置Maven 三、IDEA里配置Maven&#xff08;一&#xff09;版本情况说明&#xff08;二&#xff09;配置构建工具Maven 一、Maven概…

PyQt6 QGroupBox分组框控件

​锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计37条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话…

Python 调用企业微信群机器人发送消息及文件

Python 操作企业微信群机器人。 企业微信群创建机器人 &#xff1a; 安装 requests json &#xff1a; pip install requests pip install json发送消息&#xff08;markdown&#xff09; import requests import json# 企业微信机器人发送文字 def send_markdown (message, …

el-table分页时多选数据的保存和回显

大致思路&#xff1a; 把所有选择的数据全部存到一个大数组中&#xff0c;切页的时候匹配原数据利用ref节点的.toggleRowSelection方法进行回显 具体步骤&#xff1a; 1、勾选和全选时需要判断是选中还是取消&#xff0c;然后更新大数组数据。 2、分页获取新数据之后匹配当…

初学Python基础后,如何制定学习计划?

如果你是Python小白&#xff0c;学完基础语法是个了不起的成就&#xff0c;但是接下来应该干嘛呢&#xff1f;你应该学习哪些内容&#xff1f;你应该如何规划你的Python学习路线&#xff1f; 其实这些问题的答案都取决于你的个人目标和兴趣。你想要用Python做什么&#xff1f;…

TIA博途中快速修改变量值的方法和技巧

TIA博途中快速修改变量值的方法和技巧 如下图所示,正常情况下选中该变量,然后右击选择“修改”—然后选择修改为0或1, 快速调试技巧: 如下图所示,鼠标选中该变量上方的“FALSE”,直接双击,系统会提示是否进行切换该变量的值, 点击“是”即可切换变量的值, 如下图所示,…

142. 环形链表 II

142. 环形链表 II 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;错误经验吸取 原题链接&#xff1a; 142. 环形链表 II https://leetcode.cn/problems/linked-list-cycle-ii/description/ 完成情况&#xff1a; 解题思路&#xff1a;…

CSS、JS文件无法正确加载至页面问题与解决

目录 1. 问题出现 2. 分析与解决 3. 总结 1. 问题出现 自己在写项目是时候&#xff0c;想启动浏览器查询首页面index.jsp的显示效果 预期效果应该是下面这样的&#xff1a; 但是实际上是这样的&#xff1a; 意思也就是说可能是关于CSS、JS相关的引入方面出了问题&#xff…

qt使用wimlib-imagex,做windows系统备份还原

wimlib-imagex是个第三方工具&#xff0c;可对系统映像进行操作&#xff0c;下载地址&#xff1a; https://wimlib.net/downloads/index.html 程序主要用到以下这两个文件&#xff1a;libwim-15.dll和wimlib-imagex.exe wimlib-imagex.exe的调用命令参数&#xff0c;可以通过…

Win环境中安装Jenkins指南

目录 安装Java环境 下载并安装Jenkins Jenkins版本 启动Jenkins 如何删除Jenkins 安装Java环境 访问 Oracle官方网站 下载并安装JDK 安装完成后&#xff0c;设置系统环境变量 JAVA_HOME 到你的 JDK 安装路径&#xff0c;并将 %JAVA_HOME%\bin 添加到系统 PATH 中。 下载…

SAP中的新旧事务码

SAP中的新旧事务码 SAP随着新版本的发布&#xff0c;我们知道sap已经更新了很多的程序和TCODE。sap提供了很多新的TCODE来替换旧的TCODE&#xff0c;新TCODE有很多的新特性和新功能。在这个这种情况下&#xff0c;很多旧TCODE就会被废弃。我们如何查找这个替换呢&#xff1f; …

什么是国际语音呼叫中心?国际语音呼叫中心能干什么?

1.什么是国际语音呼叫中心&#xff1f; 国际语音呼叫中心是指利用语音技术提供咨询、客服、销售、市场调研等呼叫中心服务的一种解决方案。与传统的呼叫中心相比&#xff0c;国际语音呼叫中心采用了更加高效、智能的呼叫技术&#xff0c;通过应用智能语音识别技术、自然语言处…