IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> dubbo源码学习(二)——实现核心:SPI机制 -> 正文阅读

[网络协议]dubbo源码学习(二)——实现核心:SPI机制

1. SPI机制简介

SPI机制——即JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制,dubbo基于此机制作了增强版实现,同时该机制也是实现dubbo微内核+插件模式设计思想的实现基石。

首先明确什么是扩展点,扩展点实际上就是一个接口,该接口中定义了一个待实现的方法,SPI机制实现了可以在代码运行过程中自动装配该接口的实现类。

优化点如下:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过?getName()?获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。(但这个其实不是重点)

首先,dubbo默认约定会自动加载该目录下的文件配置?META-INF/dubbo/接口全限定名,文件内容为:配置名=扩展实现类的全限定名,多个实现类用换行符分隔。

配置文件不一定都是?META-INF/dubbo/? 目录下,dubbo扩展类加载扫描的时候实际上有三种加载策略,对应会扫描三个文件目录,分别是

DUBBO_INTERNAL_STRATEGY 对应 META-INF/dubbo/internal/
DUBBO_STRATEGY 对应 META-INF/dubbo/
SERVICES_STRATEGY 对应 META-INF/services/

比如以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置配置文本文件为:

配置的文本内容为:

意思也就是说,加载一个name为 dubbo 的扩展实现类到内存中,具体类名为后面的全路径类名

org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现。比如:

<dubbo:protocol name="dubbo" />

这样就可以指定我们到底使用扩展点的哪个具体实现类了。

2. dubbo中SPI实现细节

org.apache.dubbo.common.extension.ExtensionLoader_Adaptive_Test

org.apache.dubbo.common.extension.ExtensionLoaderTest

可以结合dubbo源码中这两个测试类的测试用例来理解dubbo中SPI的使用与原理。

2.1 关键注解

(1)@SPI:通过该注解来标识一个扩展点接口,只有在扩展点接口上标识该注解,扩展点才会生效,否则就会报错。

该注解中有个可选配置一个value值,该值代表的是默认情况下所使用的的具体扩展点实现类的名称,注意,该名称要与扩展点实现类配置在META-INF/dubbo/? 目录下的配置文件中的key名称相同,如果不存在则会报错。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

    /**
     * default extension name
     */
    String value() default "";

}

@SPI("group")
public interface ActivateExt1 {
    String echo(String msg);
}

?这个配置就表示会默认采用group对应的扩展点实现类

(2)@Adaptive:自适应注解,该注解一般会添加在扩展点接口中的方法之上,标识该方法的实现会在运行过程中依据URL(该对象中包含了一次RPC调用过程中的所有相关参数,包括方法名,调用参数等信息)中的参数(key-value)进行自动适配,所以每个扩展点接口中的方法参数都必须要有URL参数,哪怕是某个参数对象中包含有URL属性对象也可以,@Adaptive注解标识的方法直到运行时才知道具体执行的扩展点实现类。当然,该注解也可以添加在实现类之上,标识该类就是自适应扩展点实现类。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    /**
     * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
     * in the URL, and the parameter names are given by this method.
     * <p>
     * If the specified parameters are not found from {@link URL}, then the default extension will be used for
     * dependency injection (specified in its interface's {@link SPI}).
     * <p>
     * For example, given <code>String[] {"key1", "key2"}</code>:
     * <ol>
     * <li>find parameter 'key1' in URL, use its value as the extension's name</li>
     * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
     * <li>use default extension if 'key2' doesn't exist either</li>
     * <li>otherwise, throw {@link IllegalStateException}</li>
     * </ol>
     * If the parameter names are empty, then a default parameter name is generated from interface's
     * class name with the rule: divide classname from capital char into several parts, and separate the parts with
     * dot '.', for example, for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, the generated name is
     * <code>String[] {"yyy.invoker.wrapper"}</code>.
     *
     * @return parameter names in URL
     */
    String[] value() default {};

}

同样的,@Adaptive注解也有一个可选配置value项,该项配置标识该扩展点自动适配所依据URL对象中的key名称是什么,如果不配置的话默认是首字母小写的类名作为默认值。会依据该key字段的值来找到对应的扩展点实现类。具体如下代码所示

//扩展点接口
@SPI
public interface Ext2 {
    // UrlHolder类型的参数对象中,持有一个URL类型的属性变量
    @Adaptive
    String echo(UrlHolder holder, String s);

    String bang(URL url, int i);
}

//扩展点实现类
public class Ext2Impl1 implements Ext2 {
    public String echo(UrlHolder holder, String s) {
        return "Ext2Impl1-echo";
    }

    public String bang(URL url, int i) {
        return "bang1";
    }
}

然后将扩展点实现类配置到配置文件中

impl1=org.apache.dubbo.common.extension.ext2.impl.Ext2Impl1

此时就代表着,URL对象中包含的参数必须持有一个key为ext2,value为impl1的k-v参数,才能够获取到自适应的扩展点实现。

    @Test
    public void test_urlHolder_getAdaptiveExtension() throws Exception {
        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();

        Map<String, String> map = new HashMap<String, String>();
        //此处就可以获取到自适应的扩展点实现。
        map.put("ext2", "impl1");
        URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);

        UrlHolder holder = new UrlHolder();
        holder.setUrl(url);

        String echo = ext.echo(holder, "haha");
        assertEquals("Ext2Impl1-echo", echo);
    }

(3)@Activate:自动激活注解,该注解会放置在扩展点接口对应的实现类(或者方法)之上,标识该实现类会自动激活加载(当然,该类必须配置在配置文件中,也就是?META-INF/dubbo/? 目录下添加该扩展点接口配置的文件中),一般用于dubbo中的Filter模块加载。

同样的,该注解也有几个配置值,如果不做任何配置,就表示该扩展点实现无条件激活生效。

  • group:取值范围是provider或consumer,表示只对服务提供方或服务消费方生效。
  • value:作用和上面的几个注解一样,都是从URL参数中提取出参数的key值,如果URL中不存在该key,则该扩展点实现不生效。
  • order:表示生效顺序。
  • 其他两个自2.7版本以后就被删除了,而且本身很少使用,可以不用考虑这两个数值
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    
    String[] group() default {};

    
    String[] value() default {};

    /**
     * Relative ordering info, optional
     * Deprecated since 2.7.0
     *
     * @return extension list which should be put before the current one
     */
    @Deprecated
    String[] before() default {};

    /**
     * Relative ordering info, optional
     * Deprecated since 2.7.0
     *
     * @return extension list which should be put after the current one
     */
    @Deprecated
    String[] after() default {};

   
    int order() default 0;
}

使用范例(可以参考org.apache.dubbo.cache.filter.CacheFilter的实现案例)

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
 
@Activate // 无条件自动激活
public class XxxFilter implements Filter {
    // ...
}


import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
 
@Activate("xxx") // 当配置了xxx参数,并且参数为有效值时激活
public class XxxFilter implements Filter {
    // ...
}



@Activate(group = "provider", value = "xxx") // 只对提供方激活
public class XxxFilter implements Filter {
    // ...
}

?(4)扩展点自动包装类:

自动包装扩展点的 Wrapper 类。ExtensionLoader?在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。

import org.apache.dubbo.rpc.Protocol;
 
public class XxxProtocolWrapper implements Protocol {
    Protocol impl;
 
    public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
 
    // 接口方法做一个操作后,再调用extension的方法
    public void refer() {
        //... 一些操作
        impl.refer();
        // ... 一些操作
    }
 
    // ...
}

Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从?ExtensionLoader?返回扩展点时,包装在真正的扩展点实现外。即从?ExtensionLoader?中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。

扩展点的 Wrapper 类可以有多个,也可以根据需要新增。

通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点实现类。

2.1 核心实现类ExtensionLoader源码阅读

ExtensionLoader的使用一般为:

//获取所有自动激活的扩展点实现,也就是配置的@Activate注解类
        URL url = URL.valueOf("test://localhost/test");
        List<ActivateExt1> list = getExtensionLoader(ActivateExt1.class)
                .getActivateExtension(url, new String[]{}, "default_group");

//获取自适应的扩展点实现类
        ExtensionLoader<AddExt2> loader = getExtensionLoader(AddExt2.class);
        AddExt2 adaptive = loader.getAdaptiveExtension();

//依据配置的扩展点实现类对应的key,获取实现类
        WrappedExt impl1 = getExtensionLoader(WrappedExt.class).getExtension("impl1");

//获取默认扩展点实现类
        SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension();

(1)public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)

依据扩展点接口类Class对象,来获取扩展点加载器,这是最初的入口方法,这里会有一层缓存,缓存每一个已经生成的Class对应的扩展点加载器ExtensionLoader对象。

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        //type必须为接口类型
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        //判断接口上是否有@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
        //先通过缓存获取该type对应的加载器
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        //如果缓存中没有,则进行创建一个
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

    //这里就是ExtensionLoader的构造方法,可以看到这里还调用了ExtensionFactory的扩展实现
    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

2)public T 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();
                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);
                    }
                }
            }
        }

        return (T) instance;
    }



    private T createAdaptiveExtension() {
        try {
            //三层代码,最外层的injectExtension负责对最后生成的实例对象进行属性注入
            //getAdaptiveExtensionClass负责获取自适应的扩展类Class对象,核心方法
            //然后依据这个Class对象调用newInstance方法获取实例对象
            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();
        //当调用完 getExtensionClasses方法后,如果存在自适应的扩展点类,会被赋值给cachedAdaptiveClass,那么直接返回,
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        //如果没有找到被@Adaptive标注的类,则在这里创建自适应类,通过字符串拼接生成一个类并加载到jvm中
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }


    private Class<?> createAdaptiveExtensionClass() {
        //这里通过代码生成器生成目标扩展点的具体实现类
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        //然后通过类加载器和编译器编译并加载这个类字符串到jvm中
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

最后,通过代码生成器生成的自适应类源码如下示例所示:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	public com.alibaba.dubbo.rpc.Exporter (com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
        //这里的extName的获取是生成的代码,参考org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generateExtNameAssignment()方法中的逻辑
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        //在这里通过extName获取具体的扩展点实现类对象实例
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        //调用该对象实例对应的扩展点方法
		return extension.export(arg0);
	}
	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg1 == null) throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
}

通过以上代码分析可以看到,自适应扩展类的获取实现思路其实有些类似于代理模式加策略模式,通过字符串拼接生成当前扩展点自适应实现类的代码,其代理了扩展点接口方法,但其中的扩展点方法并不做具体实现,而是依据扩展点方法的URL参数中的的关键key对应的value值来决定到底使用哪个配置的扩展点实现类。

(3)private Map<String, Class<?>> getExtensionClasses():核心方法,获取并加载META-INF目录下的配置文件中配置的扩展点对应的具体实现类,上面几个方法内部都会调用该方法来获取扩展点实现类。

    private Map<String, Class<?>> getExtensionClasses() {
        //同样的,先从缓存中尝试获取缓存的所有扩展点实现类Class
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    //如果没有获取到,则进行类加载操作
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }


    private Map<String, Class<?>> loadExtensionClasses() {
        //这个作用不大,主要是获取扩展点上@SPI注解中设置的value值
        cacheDefaultExtensionName();
        //这里存储最后所有扫描加载到的扩展点实现类,key是配置文件中配置的=前面的字符串
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        //这里的三个扫描加载策略实际上就是三种配置文件目录,分别是META-INF/dubbo/、META-INF/services/、META-INF/dubbo/internal/
        for (LoadingStrategy strategy : strategies) {
            //核心方法loadDirectory,负责将指定目录下的配置文件中指定的扩展类进行加载
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
        }

        return extensionClasses;
    }



    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, String... excludedPackages) {
        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);
                }
            }

            //上面一大段其实就是为了拿到所有包下META-INF/中扩展点对应的配置文件的路径(dubbo规定配置文件必须是扩展点接口全路径命名)
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    //遍历解析每个文件路径,读取其中配置的文本数据,并加载类,这里是核心方法
                    loadResource(extensionClasses, classLoader, resourceURL, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }



    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                              java.net.URL resourceURL, String... excludedPackages) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                //读取文件
                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();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
                                //然后拿到类的全路径名进行加载类
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } 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);
        }
    }


    private void loadClass(Map<String, Class<?>> 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注解的类添加到自适应缓存中
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
            //wrapper扩展类缓存放在这里
            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注解的扩展类缓存处理
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }

(4)public T getExtension(String name):依据URL中传递的参数值,获取对应的扩展点实现类对象。

    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        //如果值为true,则直接返回默认扩展点实现类对象实例即可
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //从缓存中获取
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    //获取失败则创建,核心方法,里面没什么特殊逻辑,也是通过getExtensionClasses方法获取加载配置的所有扩展点实现类,然后实例化
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

(5)public T getDefaultExtension():获取默认实现类

    public T getDefaultExtension() {
        //先尝试加载扩展点对应的所有实现类
        getExtensionClasses();
        //然后看一下有没有设置默认扩展点实现类,没有就直接退出
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        //有的话,就通过cachedDefaultName获取
        return getExtension(cachedDefaultName);
    }

(6)public List<T> getActivateExtension(URL url, String[] values, String group)

依据@Activate注解中配置的group和value中配置两个匹配条件,对当前扩展点的所有实现类进行过滤排序,然后返回满足条件的扩展类对象实例集合。

其实说白了,SPI就是通过读取指定目录下的配置文件,从配置文件中读取扩展点接口的具体实现类并加载,然后依据注解上的配置以及URL参数中的属性值来决定到底要是用哪个实现类执行相应操作。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:27:36  更:2022-05-08 08:28:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 10:37:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计