学习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接口.









![[CAD]接下来导出一张高清大图](https://img-blog.csdnimg.cn/direct/b33e3bba0fb848b28b8b1a98482cdbbc.png)







![[组合数学]LeetCode:2954:统计感冒序列的数目](https://img-blog.csdnimg.cn/f95ddae62a4e43a68295601c723f92fb.gif#pic_center)

