dubbo spi 原理分析

文章目录

  • 前言
  • 一、加载固定的扩展类
    • 1、ExtensionLoader.getExtension
    • 2、createExtension
    • 3、getExtensionClasses
    • 4、loadExtensionClasses
    • 5、cacheDefaultExtensionName
    • 6、loadDirectory
    • 7、loadResource
    • 8、loadClass
  • 二、 加载自适应扩展类
    • 1、ExtensionLoader.getAdaptiveExtension
    • 2、createAdaptiveExtension
    • 3、getAdaptiveExtensionClass
    • 4、createAdaptiveExtensionClass
    • 5、generate
    • 6、generateMethod
    • 7、generateMethodContent
    • 8、Protocol$Adaptive
  • 三、Dubbo IOC
  • 总结


前言

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。

  • Dubbo 中,SPI 主要有两种用法,一种是加载固定的扩展类,另一种是加载自适应扩展类。
  • Dubbo 中,基于 SPI 扩展加载的类是单例的。

Dubbo 的扩展能力非常灵活,在自身功能的实现上无处不在,它的原理很值得学习
文章基于3.1.0版本进行分析

		
            org.apache.dubbo
            dubbo
            3.1.0
        

dubbo spi 原理分析_第1张图片


一、加载固定的扩展类

1、ExtensionLoader.getExtension

Dubbo 中,SPI 加载固定扩展类的入口是 ExtensionLoader 的 getExtension 方法

public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        // 获取默认的拓展实现类
        return getDefaultExtension();
    }
    // Holder,顾名思义,用于持有目标对象
    Holder<Object> holder = cachedInstances.get(name);
    // 这段逻辑保证了只有一个线程能够创建 Holder 对象
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    // 双重检查 保证单例
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 创建拓展实例
                instance = createExtension(name);
                // 设置实例到 holder 中
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

上面代码的逻辑比较简单,首先检查缓存,缓存未命中则创建拓展对象

2、createExtension

创建拓展对象的过程

private T createExtension(String name, boolean wrap) {
    // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
    Class<?> clazz = getExtensionClasses().get(name);
    // 如果没有该接口的扩展,或者该接口的实现类不允许重复但实际上重复了,直接抛出异常
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        // 这段代码保证了扩展类只会被构造一次,也就是单例的.
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 向实例中注入依赖
        injectExtension(instance);

        // 如果启用包装的话,则自动为进行包装.
        // 比如我基于 Protocol 定义了 DubboProtocol 的扩展,但实际上在 Dubbo 中不是直接使用的 DubboProtocol, 而是其包装类
        // ProtocolListenerWrapper
        if (wrap) {

            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }
    
            // 循环创建 Wrapper 实例
            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                        // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                        // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
            }
        }
        // 初始化
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

createExtension 方法的逻辑稍复杂一下,包含了如下的步骤:

  • 通过 getExtensionClasses 获取所有的拓展类
  • 通过反射创建拓展对象
  • 向拓展对象中注入依赖 (Dubbo ioc)
  • 将拓展对象包裹在相应的 Wrapper 对象中
  • 初始化拓展对象

以上步骤中,第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现

3、getExtensionClasses

获取所有的拓展类

  • 根据配置文件解析出拓展项名称到拓展类的映射关系表(Map<名称, 拓展类>)
  • 根据拓展项名称从映射关系表中取出相应的拓展类
private Map<String, Class<?>> getExtensionClasses() {
    // 从缓存中获取已加载的拓展类
    Map<String, Class<?>> classes = cachedClasses.get();
    // 双重检查
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                // 加载拓展类
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

4、loadExtensionClasses

加载拓展类

  • 对 SPI 注解进行解析
  • 调用 loadDirectory 方法加载指定文件夹配置文件
private Map<String, Class<?>> loadExtensionClasses() {
    // 缓存默认的 SPI 扩展名
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();
    
    // 基于策略来加载指定文件夹下的文件
    // 目前有四种策略,分别读取 META-INF/services/ META-INF/dubbo/ META-INF/dubbo/internal/ META-INF/dubbo/external/ 这四个目录下的配置文件
    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
    }

    return extensionClasses;
}

5、cacheDefaultExtensionName

对 SPI 注解进行解析

private void cacheDefaultExtensionName() {
	// 查找存在spi注解的扩展类
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation == null) {
        return;
    }
	// 找到默认的spi
    String value = defaultAnnotation.value();
    if ((value = value.trim()).length() > 0) {
        String[] names = NAME_SEPARATOR.split(value);
        if (names.length > 1) {
            throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                + ": " + Arrays.toString(names));
        }
        if (names.length == 1) {
            cachedDefaultName = names[0];
        }
    }
}

6、loadDirectory

调用 loadDirectory 方法加载指定文件夹配置文件

  • 通过 classLoader 获取所有资源链接,
  • 通过 loadResource 方法加载资源
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                           boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
    // fileName = 文件夹路径 + type 全限定名 
    String fileName = dir + type;
    try {
        Enumeration<java.net.URL> urls = null;
		
        ClassLoader classLoader = findClassLoader();

        // try to load from ExtensionLoader's ClassLoader first
        if (extensionLoaderClassLoaderFirst) {
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                urls = extensionLoaderClassLoader.getResources(fileName);
            }
        }
        // 根据文件名加载所有的同名文件
        if (urls == null || !urls.hasMoreElements()) {
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
        }

        if (urls != null) {
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                // 加载资源
                loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}

7、loadResource

读取和解析配置文件

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                          java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
    try {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            String clazz = null;
            // 按行读取配置内容
            while ((line = reader.readLine()) != null) {
                // 定位 # 字符
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    // 截取 # 之前的字符串,# 之后的内容为注释,需要忽略
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        // 以等于号 = 为界,截取键与值
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            clazz = line.substring(i + 1).trim();
                        } else {
                            clazz = line;
                        }
                        // 加载类,并通过 loadClass 方法对类进行缓存
                        if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
                            loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                        }
                    } catch (Throwable t) {
                        IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", class file: " + resourceURL + ") in " + resourceURL, t);
    }
}

8、loadClass

操作缓存

  • cachedAdaptiveClass 缓存Adaptive类
  • cacheWrapperClass 缓存包装类
  • cacheName 缓存 Class 到名称的映射关系
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                       boolean overridden) 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 注解
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        cacheAdaptiveClass(clazz, overridden);
    } else if (isWrapperClass(clazz)) {
        // 缓存包装类
        cacheWrapperClass(clazz);
    } else {
        // 进入到这里,表明只是该类只是一个普通的拓展类
        // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
        clazz.getConstructor();
        if (StringUtils.isEmpty(name)) {
            // 如果 name 为空,则尝试从 Extension 注解中获取 name,或使用小写的类名作为 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 注解,则使用 names 数组的第一个元素作为键,
            // 存储 name 到 Activate 注解对象的映射关系
            cacheActivateClass(clazz, names[0]);
            for (String n : names) {
                // 存储 Class 到名称的映射关系
                cacheName(clazz, n);
                // 存储 name 到 Class 的映射关系.
                // 如果存在同一个扩展名对应多个实现类,基于 override 参数是否允许覆盖,如果不允许,则抛出异常.
                saveInExtensionClass(extensionClasses, clazz, n, overridden);
            }
        }
    }
}

二、 加载自适应扩展类

自适应扩展类基于参数,在运行时动态选择到具体的目标类执行,参数必须带有URL或者参数的内部包含URL
为了实现有些拓展不在框架启动阶段被加载,而是在拓展方法被调用时,根据运行时参数进行加载

1、ExtensionLoader.getAdaptiveExtension

检查缓存,缓存未命中则创建拓展对象

public T getAdaptiveExtension() {
    // 从缓存中获取自适应拓展
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        // 如果存在异常,则直接抛出
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " +
                    createAdaptiveInstanceError.toString(),
                    createAdaptiveInstanceError);
        }

        synchronized (cachedAdaptiveInstance) {
            instance = cachedAdaptiveInstance.get();
            // double check
            if (instance == null) {
                try {
                    // 创建自适应拓展
                    // 这里分为两种情况:一种是存在 Adaptive 类,另一个是需要生成 Adaptive 类
                    instance = createAdaptiveExtension();
                    cachedAdaptiveInstance.set(instance);
                } catch (Throwable t) {
                    createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }

    return (T) instance;
}

2、createAdaptiveExtension

private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ...");
    }
}

createAdaptiveExtension 包含了三个逻辑

  • 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
  • 通过反射进行实例化
  • 调用 injectExtension 方法向拓展实例中注入依赖

3、getAdaptiveExtensionClass

获取自适应扩展类

private Class<?> getAdaptiveExtensionClass() {
    // 通过 SPI 获取所有的拓展类
    getExtensionClasses();
    // 检查缓存,若缓存不为空,则直接返回缓存
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应拓展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getAdaptiveExtensionClass 方法同样包含了三个逻辑

  • 调用 getExtensionClasses 获取所有的拓展类
  • 检查缓存,若缓存不为空,则返回缓存
  • 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类

4、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);
}

该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例

5、generate

自适应拓展类代码生成

public String generate() {
    // 如果该接口中没有方法被 @Adaptive 注解修饰,直接抛出异常
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    // 生成包名、import、方法等.
    code.append(generatePackageInfo());
    code.append(generateImports());
    code.append(generateClassDeclaration());

    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    code.append("}");

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    return code.toString();
}

6、generateMethod

生成方法的逻辑是最关键的

private String generateMethod(Method method) {
    String methodReturnType = method.getReturnType().getCanonicalName();
    String methodName = method.getName();
    // 生成方法内容
    String methodContent = generateMethodContent(method);
    String methodArgs = generateMethodArguments(method);
    String methodThrows = generateMethodThrows(method);
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

7、generateMethodContent

生成方法内容

private String generateMethodContent(Method method) {
    // 该方法上必须有 @Adaptive 注解修饰
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    if (adaptiveAnnotation == null) {
        // 没有 @Adaptive 注解修饰,生成异常信息
        return generateUnsupported(method);
    } else {
        // 获取 URL 在参数列表上的索引
        int urlTypeIndex = getUrlTypeIndex(method);
        
        if (urlTypeIndex != -1) {
            // 如果参数列表上存在 URL,生成对 URL 进行空检查
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            // 如果参数列表不存在 URL 类型的参数,那么就看参数列表上参数对象中是否包含 getUrl 方法
            // 有的话,生成 URL 空检查
            code.append(generateUrlAssignmentIndirectly(method));
        }
        // 解析 Adaptive 注解上的 value 属性
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
        // 如果参数列表上有 Invocation 类型的参数,生成空检查并获取 methodName.
        boolean hasInvocation = hasInvocationArgument(method);
        
        code.append(generateInvocationArgumentNullCheck(method));
        // 这段逻辑主要就是为了生成 extName(也就是扩展名)
        // 分为多种情况:
        // 1.defaultExtName 是否存在
        // 2.参数中是否存在 invocation 类型参数
        // 3.是否是为 protocol 生成代理
        // 为什么要对 protocol 单独考虑了?因为 URL 中有获取 protocol 值的方法
        code.append(generateExtNameAssignment(value, hasInvocation));
        // check extName == null?
        code.append(generateExtNameNullCheck(value));
    
        // 生成获取扩展(使用 ExtensionLoader.getExtension 方法)
        code.append(generateExtensionAssignment());

        // 生成返回语句
        code.append(generateReturnAndInvocation(method));
    }

    return code.toString();
}

上面那段逻辑主要做了如下几件事

  • 检查方法上是否 Adaptive 注解修饰
  • 为方法生成代码的时候,参数列表上要有 URL(或参数对象中有 URL)
  • 使用 ExtensionLoader.getExtension 获取扩展
  • 执行对应的方法

8、Protocol$Adaptive

Protocol自适应扩展类,动态生成的代码

@SPI("dubbo")
ublic class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
	// 不是自适应方法,报错
	public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
	// 不是自适应方法,报错
    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
	
    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        // 从参数中获取加载类
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }

    public java.util.List getServers() {
        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
}

三、Dubbo IOC

通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有setter特征的方法,则通过 ObjectFactory 获取依赖对象,通过反射调用 setter 方法将依赖设置到目标对象中。

private final ExtensionFactory objectFactory;
private T injectExtension(T instance) {

    if (objectFactory == null) {
        return instance;
    }

    try {
        // 遍历目标类的所有方法
        for (Method method : instance.getClass().getMethods()) {
            // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
            if (!isSetter(method)) {
                continue;
            }
            /**
             * 检测是否有 DisableInject 注解修饰.
             */
            if (method.getAnnotation(DisableInject.class) != null) {
                continue;
            }
            
            /**
             * 检测是否实现了ScopeModelAware、ExtensionAccessorAware类,如果实现则不注入
             */
            if (method.getDeclaringClass() == ScopeModelAware.class) {
                    continue;
            }
            if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
                if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
                    continue;
                }
            }
            
            // 基本类型不注入
            Class<?> pt = method.getParameterTypes()[0];
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                // 获取属性名,比如 setName 方法对应属性名 name
                String property = getSetterProperty(method);
                // 从 ObjectFactory 中获取依赖对象
                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;
}

ExtensionFactory

  • SpiExtensionFactory , 用于创建自适应的拓展
  • SpringExtensionFactory ,用于从 Spring 的 IOC 容器中获取所需的拓展

总结

Dubbo SPI中有很好的设计思路,可以通过这些设计思路更好的运用到我们的框架设计中,让我们的框架更加健壮

你可能感兴趣的:(dubbo,spring,java,spring,boot,后端)