Dubbo|深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析

目录?????
前言
一、Dubbo SPI&Java SPI的脑图
【Dubbo|深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析】二、Java SPI
1. 代码应用
2.源码分析
3.问题分析
三、Dubbo SPI
1、基础应用
2、源码分析

总结


前言 SPI是什么?解决了什么问题?Dubbo 为什么要实现SPI?Java SPI有什么问题?
看完本文您应该对此有个大致了解,并且Dubbo SPI作为Dubbo核心机制,了解Dubbo SPI的使用与实现原理,对我们深入理解Dubbo的整体设计思想会很大的帮助。

一、Dubbo SPI&Java SPI的脑图 先上图,方便大家快速定位所需内容(如果脑图实用,欢迎点赞留言)。


二、Java SPI 1. 代码应用

  1. 定义接口类:
    public interface JavaSPIInterface { String echo(String msg); }

  2. 定义实现类:
    // 以下3个实现类应该分为三个Java文件,为了方便我放在了一个代码段中 // 实现类1 public class JavaSPIInterfaceImpl1 implements JavaSPIInterface { @Override public String echo(String msg) { return "JavaSPIInterfaceImpl1 -- " + msg; } }// 实现类2 public class JavaSPIInterfaceImpl2implements JavaSPIInterface { @Override public String echo(String msg) { return "JavaSPIInterfaceImpl2 -- " + msg; } }// 实现类3 public class JavaSPIInterfaceImpl3 implements JavaSPIInterface { @Override public String echo(String msg) { return "JavaSPIInterfaceImpl3 -- " + msg; } }

  3. 配置扩展点,配置文件存META-INF/services/com.xxx.JavaSPIInterface(即接口的全路径):
    # 扩展点接口实现的全路径 com.xxx.impl.JavaSPIInterfaceImpl1 com.xxx.impl.JavaSPIInterfaceImpl2 com.xxx.impl.JavaSPIInterfaceImpl3

  4. 单元测试类
    ServiceLoader javaSPIInterfaces = ServiceLoader.load(JavaSPIInterface.class); Iterator iterator = javaSPIInterfaces.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next().echo("sks-9527")); }

    如上的4个步骤就是Java SPI的核心流程。
2.源码分析
  1. ServiceLoader.load(JavaSPIInterface.class); 源码解析:
    public staticServiceLoader load(Class service, ClassLoader loader) { return new ServiceLoader<>(service, loader); } ..... private ServiceLoader(Class svc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null"); loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; // 核心方法,重新加载扩展点配置 reload(); } ..... public void reload() { providers.clear(); //最终是创建了一个LazyIterator 实例,此时【并没有】发生扩展点文件的加载以及扩展点实现类的实例化 lookupIterator = new LazyIterator(service, loader); } ..... private LazyIterator(Class service, ClassLoader loader) { this.service = service; this.loader = loader; }

  2. LazyIterator#hasNext源码解析:
    public Iterator iterator() { return new Iterator() { // 获取SerivceLoader属性LinkedHashMap providers的迭代器, //LinkedHashMap providers用于存储扩展点的实例(key为实现类的全路径,value为对应实例) Iterator knownProviders = providers.entrySet().iterator(); public boolean hasNext() { // knownProviders.hasNext() 为true,表示缓存存在数据,不需要lookupIterator进行再次加载。 // 为false,则需要加载扩展点配置来判断是否存在下一个实例。 if (knownProviders.hasNext()) return true; // 加载扩展点配置文件 return lookupIterator.hasNext(); }public S next() { // 缓存实例存在数据,则直接返回,避免重复实例化扩展点实现 if (knownProviders.hasNext()) return knownProviders.next().getValue(); // 加载扩展点实现 return lookupIterator.next(); }public void remove() { throw new UnsupportedOperationException(); }}; }....... // lookupIterator.hasNext(); 最终调用如下代码 private boolean hasNextService() { if (nextName != null) { return true; } // Enumeration configs 扩展点的文件资源, // 为null时则进行加载,不为null,则进行获取扩展点文件的内容 // 很明显——【线程不安全的操作】 if (configs == null) { try { String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } // pending 扩展点配置类全路径字符串的迭代器 while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } // 解析配置文件内容 pending = parse(service, configs.nextElement()); } // 获取扩展点实现类的下一个全路径字符串, // 在lookupIterator#next中会根据nextName进行扩展点实现类的实例化 nextName = pending.next(); return true; } ..... // 加载扩展点配置文件内容 private Iterator parse(Class service, URL u) throws ServiceConfigurationError { InputStream in = null; BufferedReader r = null; // 存储扩展点配置文件每一行(扩展点实现类的全路径)的字符串 ArrayList names = new ArrayList<>(); try { in = u.openStream(); r = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(service, u, r, lc, names)) >= 0); } catch (IOException x) { fail(service, "Error reading configuration file", x); } finally { try { if (r != null) r.close(); if (in != null) in.close(); } catch (IOException y) { fail(service, "Error closing configuration file", y); } } // 返回扩展点全路径集合的迭代器 return names.iterator(); }

  3. LazyIterator#nex
    public Iterator iterator() { return new Iterator() {Iterator knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); }public S next() { // 如果已经存在缓存后的实例,则不会重复进行扩展点的实例化 if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); }public void remove() { throw new UnsupportedOperationException(); }}; } .... // lookupIterator.next() 分析,最终会调用nextService进行类的实例化 private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); // nextName为在LazyIterator#hasNext中下一个扩展点的全路径字符串 String cn = nextName; nextName = null; Class c = null; try { // 字符串加载为Class c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn+ " not a subtype"); } // 很明显——【线程不安全的操作】 try { // 调用默认构造方法实例化,并转换为扩展点接口 S p = service.cast(c.newInstance()); // 缓存扩展点接口实现的实例 providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }


  4. Java SPI的核心流程在于对扩展点配置文件的加载以及判断是否存在下一个扩展点实现类,下图为ServiceLoader返回的迭代器的hasNext方法的流程图。
    Dubbo|深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析
    文章图片


  5. 代码结构如图Dubbo|深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析
    文章图片
3.问题分析
  1. 资源消耗
    1. Java SPI在加载扩展点实现类的时候,不能根据实际所需来获取扩展点实现,只有加载并实例化完所有的扩展点实现后,才能获取到自己所需的实现类,如果实现类中有特别消耗资源的实例,但实际应用中并不使用,这将是非常浪费资源的。
  2. 灵活性不足
    1. 其实原因同上,只有加载完扩展点的所有实现后,才能判断实际所需的实现类。
  3. 线程不安全
    1. 在Java SPI的源码分析中,注释中线程非安全的操作有响应文字提示。
    2. 将扩展点测试类修改为如下所示
      ServiceLoader javaSPIInterfaces = ServiceLoader.load(JavaSPIInterface.class); new Thread(() -> { Iterator iterator = javaSPIInterfaces.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next().echo("sks-9527")); } }).start(); Iterator iterator = javaSPIInterfaces.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next().echo("sks-9527")); }多次运行结果如下/ java.util.NoSuchElementException at sun.misc.CompoundEnumeration.nextElement(CompoundEnumeration.java:59) at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:357) at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393) at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474) at com.xxx.JavaSPITest.testJavaSpi(JavaSPITest.java:24) // 原因 因为java.util.ServiceLoader#iterator返回的迭代器,共享了一个LazyIterator lookupIterator实例, 而在LazyIterator中的属性 Enumeration configs也是共享的,非线程安全的。


三、Dubbo SPI 1、基础应用
  1. 自定义接口
    // @SPI 标记这个接口是一个Dubbo SPI接口,value值表示这个SPI的默认实现的key // 注解的value值 对应扩展点配置文件的key @SPI("impl1") public interface DubboSPIInterface { String echo(String message); // @Adaptive value值表示根据入参 URL的Parameter动态获取扩展点实现类 // 如果没有则使用注解@SPI的value值 @Adaptive(value = https://www.it610.com/article/{"k1", "k2"}) String adaptive(URL url, String message); }

  2. 自定义实现类
    // 以下的个类分别为Java类 public class DubboSPIInterfaceImpl1 implements DubboSPIInterface { @Override public String echo(String message) { return "DubboSPIInterfaceImpl1 -- " + message; }@Override public String adaptive(URL url, String message) { return "adaptive default " + message; } }@Adaptive // 扩展点实现类只能有一个实现类标记此注解,多个类标记会抛出异常, // !!测试自适应扩展点时需要注释调@Adaptive,后面的源码分析会说原理!!! public class DubboSPIInterfaceImpl2 implements DubboSPIInterface { @Override public String echo(String message) { return "DubboSPIInterfaceImpl2 -- " + message; }@Override public String adaptive(URL url, String message) { return "adaptive default " + message; } }@Activate // 不传入参数默认激活 public class DubboSPIInterfaceImpl3 implements DubboSPIInterface { @Override public String echo(String message) { return "DubboSPIInterfaceImpl3 -- " + message; } @Override public String adaptive(URL url, String message) { return "adaptive default " + message; } }@Activate(value = https://www.it610.com/article/{"k1"}) // URL参数包含k1匹配 public class DubboSPIInterfaceImpl4 implements DubboSPIInterface {@Override public String echo(String message) { return "DubboSPIInterfaceImpl4 -- " + message; }@Override public String adaptive(URL url, String message) { return "adaptive default " + message; } }@Activate(value = https://www.it610.com/article/{"k1"} ,group = {"g1"}) // g1分组。URL包含k1激活 public class DubboSPIInterfaceImpl5 implements DubboSPIInterface {@Override public String echo(String message) { return "DubboSPIInterfaceImpl5 -- " + message; }@Override public String adaptive(URL url, String message) { return "adaptive default " + message; } }// 包装类——满足实现扩展点接口,构造函数入参包含扩展点接口(其实这就是Dubbo AOP特性的实现) public class DubboSPIInterfaceWrapper1 implements DubboSPIInterface {private DubboSPIInterface dubboSPIInterface; public DubboSPIInterfaceWrapper1(DubboSPIInterface dubboSPIInterface) { this.dubboSPIInterface = dubboSPIInterface; }@Override public String echo(String message) { System.out.println("DubboSPIInterfaceWrapper1包装开始"); return dubboSPIInterface.echo(message); } @Override public String adaptive(URL url, String message) { System.out.println("DubboSPIInterfaceWrapper1#包装开始"); return dubboSPIInterface.adaptive(url, message); } }public class DubboSPIInterfaceWrapper2 implements DubboSPIInterface {private DubboSPIInterface dubboSPIInterface; public DubboSPIInterfaceWrapper2(DubboSPIInterface dubboSPIInterface) { this.dubboSPIInterface = dubboSPIInterface; }@Override public String echo(String message) { System.out.println("DubboSPIInterfaceWrapper2包装开始"); return dubboSPIInterface.echo(message); }@Override public String adaptive(URL url, String message) { System.out.println("DubboSPIInterfaceWrapper2包装开始"); return dubboSPIInterface.adaptive(url, message); } }// 自适应扩展点实现1 public class AdaptiveDubboSPIInterfaceImpl implements DubboSPIInterface { @Override public String echo(String message) { return null; }@Override public String adaptive(URL url, String message) { return "AdaptiveDubboSPIInterfaceImpl1 -- " + message; } }public class AdaptiveDubboSPIInterfaceImpl2 implements DubboSPIInterface { @Override public String echo(String message) { return null; }@Override public String adaptive(URL url, String message) { return "AdaptiveDubboSPIInterfaceImpl2 -- " + message; } }

  3. 配置文件,Dubbo SPI的扩展点加载目录从脑图可以看到存在三个目录,分别是:META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/,配置文件名称为接口的全类名。
    impl1=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceImpl1 impl2=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceImpl2 impl3=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceImpl3 impl4=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceImpl4 impl5=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceImpl5 adaptive1=com.iths.common.dubbo.spi.impl.AdaptiveDubboSPIInterfaceImpl adaptive2=com.iths.common.dubbo.spi.impl.AdaptiveDubboSPIInterfaceImpl2 wrapper1=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceWrapper1 wrapper2=com.iths.common.dubbo.spi.impl.DubboSPIInterfaceWrapper2

  4. 测试代码
    @Test public void testDefaultExtension() { DubboSPIInterface defaultExtension = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getDefaultExtension(); System.out.println( defaultExtension.echo("sks-9527")); ; }@Test public void testGetExtensionByKey(){ DubboSPIInterface dubboSPIInterface = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getExtension("impl2"); System.out.println( dubboSPIInterface.echo("sks-9527")); ; }@Test public void testGetExtensionAdaptive(){ DubboSPIInterface dubboSPIInterface = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getAdaptiveExtension(); System.out.println( dubboSPIInterface.echo("sks-9527")); ; }@Test public void testGetExtensionActivate(){ // URL无参数,或参数不匹配org.apache.dubbo.common.extension.Activate.value值,只获取默认 URL defaultUrl = URL.valueOf("xxx://localhost:1122"); List activateExtension1 = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getActivateExtension(defaultUrl, ""); activateExtension1.forEach(ele -> System.out.println(ele.echo("sks-9527"))); System.out.println("==========分割线============="); // URL参数匹配org.apache.dubbo.common.extension.Activate.value值 k1,没有指定group URL defaultUrl2 = URL.valueOf("xxx://localhost:1122?k1=v1&k2=impl1,-impl3"); // 入参key传入值,如当前传入k2,表示获取URL的参数k2的value,value逗号分割表示多个激活点,-开头表示不激活, // 不传入key,表示获取URL的所有参数,但是不会对value值进行切割 List activateExtension2 = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getActivateExtension(defaultUrl2, "k2"); activateExtension2.forEach(ele -> System.out.println(ele.echo("sks-9527"))); System.out.println("==========分割线============="); URL defaultUrl3 = URL.valueOf("xxx://localhost:1122?k1=impl1"); List activateExtension3 = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getActivateExtension(defaultUrl3, "k1", "g1"); activateExtension3.forEach(ele -> System.out.println(ele.echo("sks-9527"))); }@Test public void testAdaptive(){ URL url = URL.valueOf("xxx://localhost:1122?k1=adaptive1"); String parameter = url.getParameter("k1", url.getParameter("k2", "impl1")); DubboSPIInterface defaultExtension = ExtensionLoader.getExtensionLoader(DubboSPIInterface.class).getAdaptiveExtension(); String adaptive = defaultExtension.adaptive(url, "sks-9527"); System.out.println(adaptive); }


2、源码分析 Dubbo SPI核心流程如图
Dubbo|深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析
文章图片


主要分析流程中以下几个节点的源码,
  1. 加载扩展点配置文件
  2. 自适应扩展点的动态代码编译
  3. IoC原理——即流程中的setter方法注入。
  4. AOP原理——即流程中构造包装类的过程。

  1. 加载扩展点配置文件
    // org.apache.dubbo.common.extension.ExtensionLoaderprivate Map> getExtensionClasses() { Map> classes = cachedClasses.get(); // 双重检查 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // 先加载完再设置到cachedClasses,避免拿到一半其他线程运行 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } ..... // synchronized in getExtensionClasses private Map> loadExtensionClasses() { // 缓存默认扩展点实现名称 即@SPI的value值,可能为空 cacheDefaultExtensionName(); Map> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); // .replace("org.apache", "com.alibaba")) 主要为了兼容dubbo贡献给Apache后,升级包路径问题 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }.....// 中间跳过方法: ExtensionLoader#loadDirectory、 ExtensionLoader#loadResource ..... private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } // 标记@Adaptive的Class单独处理,注意!!!类标记@Adaptive后,方法上的@Adaptive不生效 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { // 包装类缓存 cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } }String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { // 缓存@Activate 注解与对应的Class cacheActivateClass(clazz, names[0]); for (String n : names) { // 普通Class缓存 ,此处!!!!可以看出普通扩展点包含@Activate 注解的Class cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n); } } } }...... // 包装类判断条件 private boolean isWrapperClass(Class clazz) { try { clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) { return false; } }

  2. 自适应扩展点的动态代码编译与IoC
    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; } ..... private T createAdaptiveExtension() { try { // getAdaptiveExtensionClass() 获取适应类Class,调用无参构造创建实例 // #injectExtension setter方法注入 return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } ..... private Class getAdaptiveExtensionClass() { getExtensionClasses(); // cachedAdaptiveClass 存在,不进行动态代码编译!!!! if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); } ..... // 动态生成代码编译 private Class createAdaptiveExtensionClass() { // 核心——字符串拼接Java 类,下一个代码块儿会把生成的代码粘贴 // 可以断点看下内容 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,核心默认实现是Javassit return compiler.compile(code, classLoader); } ....... // !!!!!IoC实现核心 private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { // setter方法判断,如 setApple() if (isSetter(method)) { /** * Check {@link DisableInject} to see if we need auto injection for this property */ if (method.getAnnotation(DisableInject.class) != null) { continue; } // 参数类型 Class pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { // 获取名称,如setApple ---> apple String property = getSetterProperty(method); // 从Spring上下文与Spi中获取实现 // SpiExtensionFactory、SpringExtensionFactory Object object = objectFactory.getExtension(pt, property); if (object != null) { // 反射注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }

  3. 生成的动态代码
    package com.iths.common.dubbo.spi; import org.apache.dubbo.common.extension.ExtensionLoader; public class DubboSPIInterface$Adaptive implements com.iths.common.dubbo.spi.DubboSPIInterface { public java.lang.String adaptive(org.apache.dubbo.common.URL arg0, java.lang.String arg1){ if (arg0 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg0; String extName = url.getParameter("k1", url.getParameter("k2", "impl1")); if(extName == null) throw new IllegalStateException("Failed to get extension (com.iths.common.dubbo.spi.DubboSPIInterface) name from url (" + url.toString() + ") use keys([k1, k2])"); com.iths.common.dubbo.spi.DubboSPIInterface extension = (com.iths.common.dubbo.spi.DubboSPIInterface)ExtensionLoader.getExtensionLoader(com.iths.common.dubbo.spi.DubboSPIInterface.class).getExtension(extName); return extension.adaptive(arg0, arg1); } public java.lang.String echo(java.lang.String arg0){ throw new UnsupportedOperationException("The method public abstract java.lang.String com.iths.common.dubbo.spi.DubboSPIInterface.echo(java.lang.String) of interface com.iths.common.dubbo.spi.DubboSPIInterface is not adaptive method!"); } }

  4. AOP原理
    private T createExtension(String name) { Class clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); Set> wrapperClasses = cachedWrapperClasses; // 遍历构造wrapper类,即将构造的扩展点实现类作为wrapper类的构造参数, // 返回wrapper类实例 if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }



总结
  1. 了解Dubbo SPI的原理以及对其进行深入源码的分析,对于我们学习Dubbo的源码有相当的好处,比如在服务暴露与消费的时候,是怎样动态的激活对应协议的,有了本文的基础,再去看这部分源码应该可以快速理解。
  2. 如果想做一个解耦合高度扩展框架,Dubbo SPI其实可以给我们一些思路。当前开源框架,像以前的sharding-jdbc(现在贡献给了Apache)其高扩展性也是基于SPI实现,和Dubbo SPI有异曲同工之妙。想了解可以点击链接shardingsphere SPI。
如果本文有任何问题或遗漏,或者各位有需要交流的内容,都欢迎留言交流。

    推荐阅读