前言

SPI(Service Provider Interface)是一种动态替换实现的机制,解耦接口与实现,大大提高了程序的可扩展性。对于框架来说,开闭原则(OCP)非常赞的诠释。

关SPI会分成上、下两篇来分析,本篇会从JDK、Spring、Dubbo三个SPI实现分析其原理及三者的区别;下一篇会重点介绍关于Dubbo SPI的更多特性。

SPI

分别进行简单介绍,然后通过示例切入其核心代码实现,再小结其优缺点。

JDK SPI

JDK1.6版本提供了SPI支持,作为一个简单的服务提供工具,核心类 java.util.ServiceLoader,由于其是JDK内置的,不用额外实现,所以用的场景还有较多的,例如:mysql-connector-java-8.0.22.jar的Driver的实现加载。

一个小示例(代码不多,多个类文件放在一起),示例JDK版本11:

// 接口
// top.xudj.spi.jdk.JdkSimpleSpi
public interface JdkSimpleSpi {
    void sayHello();
}


// 实现类1
// top.xudj.spi.jdk.JdkSimpleSpiImpl1
public class JdkSimpleSpiImpl1 implements JdkSimpleSpi {

    @Override
    public void sayHello() {
        System.out.println("JdkSimpleSpiImpl1 say hello");
    }
}


// 实现类2
// top.xudj.spi.jdk.JdkSimpleSpiImpl2
public class JdkSimpleSpiImpl2 implements JdkSimpleSpi {

    @Override
    public void sayHello() {
        System.out.println("JdkSimpleSpiImpl2 say hello");
    }
}


// 演示类
public class JdkSpiDemo {
    /**
     * jdk spi 调用流程:
     * 1. 通过线程上下文类加载器加载 META-INF/services/下的配置文件
     * 2. 通过配置文件中的类名实例化对象
     * 3. 调用实例化对象的方法
     */
    public static void main(String[] args) {
        ServiceLoader<JdkSimpleSpi> serviceLoader = ServiceLoader.load(JdkSimpleSpi.class);
        Iterator<JdkSimpleSpi> iterator = serviceLoader.iterator();
        while (iterator.hasNext()) {
            JdkSimpleSpi jdkSimpleSpi = iterator.next();
            jdkSimpleSpi.sayHello();
        }
    }

}

配置,在Resources下创建文件:META-INF/services/top.xudj.spi.jdk.JdkSimpleSpi

top.xudj.spi.jdk.JdkSimpleSpiImpl1
top.xudj.spi.jdk.JdkSimpleSpiImpl2

文件配置要求

1、需要特定目录:META-INF/services

2、文件名为接口/父类全限定名,文件内容为接口/子类实现全限定名,多个实现通过换行区分

运行演示类,输出:

JdkSimpleSpiImpl1 say hello
JdkSimpleSpiImpl2 say hello

原理分析,使用ServiceLoader调用load方法,传入接口Class,获取ServiceLoader对象。因ServiceLoader实现了Iterable,故可以进行遍历。下面看load方法。

// java.util.ServiceLoader#load(java.lang.Class<S>)
public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = ClassLoader.getPlatformClassLoader();
    return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
}

默认使用当前线程的ClassLoader,另Reflection.getCallerClass()返回调用当前load的类,即JdkSpiDemo。

紧接着进行迭代:Iterator<JdkSimpleSpi> iterator = serviceLoader.iterator();

public Iterator<S> iterator() {
    // 创建真实用于创建和获取对象的迭代器
    if (lookupIterator1 == null) {
        lookupIterator1 = newLookupIterator();
    }

	// 返回匿名Iterator实现类对象
    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获取
                next = lookupIterator1.next().get();
                instantiatedProviders.add(next);
            }
            index++;
            return next;
        }

    };
}

S:表示范型,此处为JdkSimpleSpi接口

index:表示当前遍历的下标

instantiatedProviders:可以理解为存放实例的缓存

lookupIterator1:真实用于创建和获取对象的迭代器

两个核心逻辑,是否有下一个:lookupIterator1.hasNext();获取下一个的实例对象: next = lookupIterator1.next().get();

先看lookupIterator1是怎么样的迭代器,lookupIterator1 = newLookupIterator(); 返回迭代器:

private Iterator<Provider<S>> newLookupIterator() {
    assert layer == null || loader == null;
    if (layer != null) {
        return new LayerLookupIterator<>();
    } else {
        Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
        Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
        return new Iterator<Provider<S>>() {
            @Override
            public boolean hasNext() {
                return (first.hasNext() || second.hasNext());
            }
            @Override
            public Provider<S> next() {
                if (first.hasNext()) {
                    return first.next();
                } else if (second.hasNext()) {
                    return second.next();
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
    }
}

实际会调用到second.hasNext(),及 second.next()。

当调用second.hasNext()时,LazyClassPathLookupIterator会加载对应META-INF/services/下的文件,并将类全限定名转换成Class对象,创建ProviderImpl类对象传入Class对象构造器;

当调用second.next()时,获取到ProviderImpl类对象,再调用ProviderImpl对象的get方法:

// java.util.ServiceLoader.ProviderImpl#newInstance
private S newInstance() {
    S p = null;
    Throwable exc = null;
    if (acc == null) {
        try {
			// 实例化
            p = ctor.newInstance();
        } catch (Throwable x) {
            exc = x;
        }
    } else {
        // ...
    }
	// ...
    return p;
}

至此完成了实现类的实例化,并返回到iterator方法中的匿名迭代器实现的next方法,并将实例化的对象加入缓存instantiatedProviders中。

大致的流程如下:

值得注意的是:

  1. 如上是JDK11的源码,从JDK9开始,出现了Stream()方法,可以通过该方法完成类型比较而不用实例化,实现了按需实例化的能力。而在JDK8及之前,会实例化全部实现,无法做到按需加载。

  2. JDK的SPI不限于针对接口,类、抽象类都可以。


Spring SPI

Spring SPI的实现比较简单,先看示例,示例Spring版本5.3.31。

// 接口,top.xudj.spi.spring.SpringSimpleSpi
public interface SpringSimpleSpi {
    void sayHello();
}

// 实现1,top.xudj.spi.spring.SpringSimpleSpiImpl1
public class SpringSimpleSpiImpl1 implements SpringSimpleSpi {
    @Override
    public void sayHello() {
        System.out.println("SpringSimpleSpiImpl1 say hello");
    }
}

// 实现2,top.xudj.spi.spring.SpringSimpleSpiImpl2
public class SpringSimpleSpiImpl2 implements SpringSimpleSpi {
    @Override
    public void sayHello() {
        System.out.println("SpringSimpleSpiImpl2 say hello");
    }
}

// 演示类
public class SpringSpiDemo {

    /**
     * Spring spi 调用流程
     * 1. 通过SpringFactoriesLoader.loadFactories方法加载SpringSimpleSpi接口的实现类
     * 2. 调用实现类的sayHello方法
     */
    public static void main(String[] args) {
        List<SpringSimpleSpi> springSimpleSpiList = SpringFactoriesLoader.loadFactories(SpringSimpleSpi.class,
                SpringSimpleSpi.class.getClassLoader());
        springSimpleSpiList.forEach(SpringSimpleSpi::sayHello);
    }
}

配置文件:META-INF/spring.factories,多个实现使用逗号分隔

top.xudj.spi.spring.SpringSimpleSpi=\
  top.xudj.spi.spring.SpringSimpleSpiImpl1,top.xudj.spi.spring.SpringSimpleSpiImpl2

由配置文件可知,Spring的SPI配置都会放到spring.factories文件中。

文件配置要求

1、需要特定目录和文件名:META-INF/spring.factories

2、文件内容为key-value方式,key表示接口的全限定名,value为接口实现全限定名,多个实现通过逗号分割

运行演示类,输出:

SpringSimpleSpiImpl1 say hello
SpringSimpleSpiImpl2 say hello

原理分析:Spring使用SpringFactoriesLoader进行加载获取配置在spring.factories的实现,并进行实例化,对应方法SpringFactoriesLoader#loadFactories

// org.springframework.core.io.support.SpringFactoriesLoader#loadFactories
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   // 获取当前类的类加载器
   if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}
   // 1、通过SpringFactoriesLoader.loadFactoryNames方法加载factoryType接口的实现类的全限定名集合
   List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
   // ...
   List<T> result = new ArrayList<>(factoryImplementationNames.size());
   // 2、进行实例化
   for (String factoryImplementationName : factoryImplementationNames) {
      // instantiateFactory通过反射构造器实例化
      result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
   }
	//...
   return result;
}

如果classLoader未传递,则使用SpringFactoriesLoader类对应的类加载器,所以这里类加载器也是关键的点,需要使用加载对应配置文件的类加载器。

该方法主要包括2步,第1步获取实现类的全限定名集合,第2步进行反射实例化。

第1步,获取某个Class的所有实现:List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);

注意,这个Class的所有实现会加载ClassLoader下的所有的spring.factories文件,而非某一个文件,然后合并对应的实现列表。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   String factoryTypeName = factoryType.getName();
   // 加载全部的spring.factories文件中的配置信息,然后根据factoryTypeName(配置文件中的key)获取对应的value集合
   return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

继续看loadSpringFactories(classLoader),完成配置文件的加载,返回Map<String, List<String>>

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   // 获取缓存中的数据
   MultiValueMap<String, String> result = cache.get(classLoader);
   // 如果缓存中有数据,则直接返回
   if (result != null) {
      return result;
   }

   try {
      // 通过类加载器加载FACTORIES_RESOURCE_LOCATION(META-INF/spring.factories)文件
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         // 获取URL
         URL url = urls.nextElement();
         // 加载配置文件,获得Properties对象
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            // 将配置文件中的配置信息放入result中
            String factoryTypeName = ((String) entry.getKey()).trim();
            // 通过逗号分隔的字符串,将其转换为List,并遍历放入result中
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               // factoryTypeName为key(全限定名),factoryImplementationName为value(全限定名)
               result.add(factoryTypeName, factoryImplementationName.trim());
            }
         }
      }
      // 将result放入缓存中,key为classLoader,所以会加载classLoader下的所有配置文件
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

优先查询缓存,缓存的key是ClassLoader,缓存中不存在,则查询该ClassLoader下全部的spring.factories文件,并循环进行处理。

返回的map,key为接口全限定名,value为spring.factories文件中配置的对应key的实现类的全部全限定名集合。

第2步,通过instantiateFactory方法完成实例化,并返回。

至此,Spring的SPI加载分析结束,相对比较简单。用的场景比较多的应该是Spring的自动装配了。和JDK一样都不限于接口。


Dubbo SPI

Dubbo的SPI相对比较复杂,在实现SPI的同时会进行依赖注入和包装(下一篇Dubbo SPI再进行介绍),同时缓存也较多。仍然从示例出发,看看Dubbo SPI是如何完成对应类实例化的。

示例Dubbo版本3.2.11,示例:

// 接口,top.xudj.spi.dubbo.DubboSimpleSpi

@SPI(value = DubboSimpleSpiImpl1.NAME, scope = ExtensionScope.APPLICATION)
public interface DubboSimpleSpi {
    void sayHello();
}

// 实现1,top.xudj.spi.dubbo.DubboSimpleSpiImpl1
public class DubboSimpleSpiImpl1 implements DubboSimpleSpi {
    public static final String NAME = "dubboSimpleSpiImpl1";
    @Override
    public void sayHello() {
        System.out.println("DubboSimpleSpiImpl1 say hello");
    }
}

// 实现2,top.xudj.spi.dubbo.DubboSimpleSpiImpl2
public class DubboSimpleSpiImpl2 implements DubboSimpleSpi {
    public static final String NAME = "dubboSimpleSpiImpl2";
    @Override
    public void sayHello() {
        System.out.println("DubboSimpleSpiImpl2 say hello");
    }
}

// 演示类
public class DubboSpiDemo {
    /**
     * Dubbo spi 调用流程
     * 1. 通过getExtensionLoader方法获取DubboSimpleSpi接口的ExtensionLoader
     * 2. 通过getExtension方法获取DubboSimpleSpi接口的实现类
     * 3. 调用实现类的sayHello方法
     */
    public static void main(String[] args) {
        ExtensionLoader<DubboSimpleSpi> extensionLoader = ApplicationModel.defaultModel().getExtensionLoader(DubboSimpleSpi.class);
        DubboSimpleSpi dubboSimpleSpi = extensionLoader.getExtension(DubboSimpleSpiImpl2.NAME);
        dubboSimpleSpi.sayHello();
    }
}

配置文件:META-INF/dubbo/top.xudj.spi.dubbo.DubboSimpleSpi,文件内容:

dubboSimpleSpiImpl1=top.xudj.spi.dubbo.DubboSimpleSpiImpl1
dubboSimpleSpiImpl2=top.xudj.spi.dubbo.DubboSimpleSpiImpl2

文件配置要求

1、文件目录支持三种,分别是META-INF/dubbo、META-INF/dubbo/internal、META-INF/services

2、文件名为接口的全限定名,内容为key-value方式,key是任意取的字符串,value为接口实现全限定名

运行演示类,输出:

DubboSimpleSpiImpl2 say hello

示例中提到ApplicationModel应用程序模型,是Dubbo3引入的领域模型,用于管理和区分不同的作用域的配置和服务,下一篇再进行分析。

原理分析

总共包括2步,第1步获取到ExtensionLoader<DubboSimpleSpi>;第2步通过ExtensionLoader(String)获取到扩展类实现对象。

第1步:获得ExtensionLoader<DubboSimpleSpi>,通过getExtensionLoader(DubboSimpleSpi.class);方法会进入ExtensionDirector#getExtensionLoader的方法,如下:

Dubbo2版本ExtensionLoader存在getExtensionLoader(Class)静态方法,目前已经标记废弃

// org.apache.dubbo.common.extension.ExtensionDirector#getExtensionLoader
@Override
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    checkDestroyed();
    // type不能为空
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }
    // type必须是接口
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    // type必须有@SPI注解
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type
                + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    }

    // 1. find in local cache,先从本地缓存中查找,extensionLoadersMap:ConcurrentMap<Class<?>, ExtensionLoader<?>>
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);

    // 从缓存中获取type对应的作用域,extensionScopeMap:ConcurrentMap<Class<?>, ExtensionScope>
    ExtensionScope scope = extensionScopeMap.get(type);
    // 如果缓存中不存在,则从SPI注解中获取作用域,并放入缓存
    if (scope == null) {
        // 默认ExtensionScope.APPLICATION
        SPI annotation = type.getAnnotation(SPI.class);
        scope = annotation.scope();
        extensionScopeMap.put(type, scope);
    }
    // 判断:如果loader为空,并且作用域是SELF,则创建一个实例
    if (loader == null && scope == ExtensionScope.SELF) {
        // create an instance in self scope
        // 创建ExtensionLoader实例,createExtensionLoader0会将loader放入extensionLoadersMap缓存
        loader = createExtensionLoader0(type);
    }

    // 2. find in parent
    if (loader == null) {
        // 如果loader为空,且存在父级扩展管理器,则先从父级扩展管理器中查找(递归)
        if (this.parent != null) {
            loader = this.parent.getExtensionLoader(type);
        }
    }

    // 3. create it
    // 父类扩展管理器中也没有找到,则创建一个实例
    // 注意:这里会先判断注解的作用域是否匹配如果父类/当前扩展管理器的作用域不匹配则不会创建
    if (loader == null) {
        loader = createExtensionLoader(type);
    }

    return loader;
}

首先进行一些判断,必须是接口,必须有@SPI注解

然后查询缓存extensionLoadersMap,如果存在,则最后返回缓存中的loader;

紧接着如果接口对应的作用域是SELF,则会进行直接创建;

最后,类似于类加载器ClassLoader,递归委派给父类加载器进行获取loader,如果父类匹配了作用域则创建,否则返回null,由子类加载器匹配作用域决定是否创建。(创建的loader会加入缓存)

例如@SPI注解为ExtensionScope.Module,则委派过程ExtensionScope.Module -> Application -> Framework

1)、先ModuleScope缓存中查找,找到返回,找不到则进入2)

2)、再Application缓存中找,找到返回,找不到则匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配进入3)

3)、再Framework缓存中找,找到返回,找不到则匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配进入4)

4)、最后回到了ModuleScope匹配注解作用域,匹配则创建ExtensionLoader实例,不匹配不创建,返回null

第2步,得到了ExtensionLoader,通过ExtensionLoader(String)获取到扩展接口的实现对象。

// org.apache.dubbo.common.extension.ExtensionLoader#getExtension(java.lang.String, boolean)
// wrap默认为true
public T getExtension(String name, boolean wrap) {
    // ...
    // 如果传入的name为true,则返回默认的扩展实例
    if ("true".equals(name)) {
        // 获取默认的扩展实例
        return getDefaultExtension();
    }
    String cacheKey = name;
    if (!wrap) {
        cacheKey += "_origin";
    }
    // 从缓存中获取扩展实例,并将holder存入cachedInstances缓存中,cachedInstances:ConcurrentMap<String, Holder<Object>>
    final Holder<Object> holder = getOrCreateHolder(cacheKey);
    Object instance = holder.get();
    // 双重检查锁
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 核心方法:真正创建扩展实例的地方
                instance = createExtension(name, wrap);
                holder.set(instance);
            }
        }
    }
    // object转换为T类型
    return (T) instance;
}

当name为“true“,调用getDefaultExtension返回默认扩展,即@SPI注解value值对应的类实现

进入核心方法:instance = createExtension(name, wrap);

// ExtensionLoader
private T createExtension(String name, boolean wrap) {
    // 获取接口的全部扩展类实现class,然后根据name获取对应的class
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        // 从缓存中获取扩展实例,extensionInstances:ConcurrentMap<Class<?>, Object>
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            // 调用createExtensionInstance方法创建扩展实例,并放入缓存
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));

            instance = (T) extensionInstances.get(clazz);
            // 前置处理,类似Spring的BeanPostProcessor#postProcessBeforeInitialization,可以在此处做一些扩展实例的前置处理
            instance = postProcessBeforeInitialization(instance, name);
            // 注入扩展实例的依赖,类似Spring的Autowired,只不过它是setter注入
            injectExtension(instance);
            // 后置处理,类似Spring的BeanPostProcessor#postProcessAfterInitialization,可以在此处做一些扩展实例的后置处理
            instance = postProcessAfterInitialization(instance, name);
        }

        // 是否需要包装,默认为true,先忽略
        // ...

        // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle
        // instance, this application may not invoke the lifecycle.initialize hook.
        // 如果扩展实现了Lifecycle接口,调用其初始化方法
        initExtension(instance);
        // 返回扩展实例
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException(
                "Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: "
                        + t.getMessage(),
                t);
    }
}

这里面包括了3个核心步骤

  1. 先从缓存extensionInstances获取,没有的话调用createExtensionInstance(clazz)进行创建

  2. 实例化后,进行前置处理、“依赖注入”、后置处理。与Spring的BeanPostProcessor如出一辙

  3. 如果扩展类实现了Lifecycle接口,调用其初始化方法

首先看createExtensionInstance(clazz),该方法就是使用构造器进行反射初始化。默认是无参构造器,但不是直接使用默认构造函数,可能还有另一个构造函数匹配了作用域模型参数。

其次实例化后,进行前置处理、“依赖注入”、后置处理。

  • 前置处理:默认无逻辑;

  • 依赖注入:类似Spring的Autowired,只不过它是setter注入,同时set方法对应的属性实例可以通过injector获取(scope bean和spi extension)。

不会进行依赖注入的情况:1)、对于DisableInject注解的属性不进行注入;2)、对于ScopeModelAware和ExtensionAccessorAware的set方法不进行注入(因为它们是通过postProcessAfterInitialization方法注入的)。

  • 后置处理:如果实例实现了 ScopeModelAware 接口,则将 scopeModel 注入到实例中,类似Spring的ApplicationContextAware等

当然,也可以通过ExtensionDirector添加自定义的List<ExtensionPostProcessor>,便会调用对应的前后置处理

最后扩展类实现了Lifecycle接口,调用其初始化方法,这个Lifecycle为Dubbo内部的接口,而非Spring。

至此,完成了实例化及一些缓存的处理。同时实现了Dubbo自己的DI。wrap其实对应这个Dubbo实现的AOP,包括ExtensionLoader的其它获取扩展类的方法,后面文章再介绍。

区别

用一个表格来做一些比对吧

维度

JDK SPI

Spring SPI

Dubbo SPI

难易程度

配置文件目录

META-INF/services/接口全限定名

META-INF/spring.factories

META-INF/dubbo/

META-INF/dubbo/internal/

META-INF/services/

三种目录下都是接口全限定名

配置文件内容

直接是扩展类的全限定名;

多个使用换行

key-value

key:接口的全限定名

value:扩展类的全限定名

多个使用逗号

key-value

key:别名,任意字符串,不要用“true”

value:扩展类的全限定名

扩展接口

接口或类

接口或类

接口,且必须有@SPI注解

按需实例化

JDK8及之前,每次都是实例化全部扩展类;

JDK9之后,可以通过stream方法进行Class过滤后实例化(按需)

实例化对应扩展接口的全部扩展类

按名称进行实例化(按需)

总结

从JDK到Spring再到Dubbo的SPI实现,都是为了解耦,将接口/类与扩展类进行分离,方便使用者进行扩展。对比下来,Dubbo的实现最为复杂,不仅实现了SPI的基础扩展,还增加了例如DI和AOP的功能,另外还有一些其它特性,可以看Dubbo系列-SPI扩展(下)文章。