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 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 一.SPI的源码解析(1) -> 正文阅读

[Java知识库]一.SPI的源码解析(1)

SPI:

1.Java SPI(Service Provider Interface)

Java SPI 使用了 策略模式,一个接口 多种实现
只需要声明接口
具体的实现并不在程序中 直接确定
而是由程序之外的配置掌控,用于具体实现的装配
步骤如下:

??? 定义一个接口 及 对应方法
??? 对接口进行实现
??? 在META/service/ 目录下,创建一个 以 接口全路径 命名的文件
??? 文件内中 为 具体实现类的 全路径名,如果有多个,用分行符 分隔
??? 在代码中 通过 java.util.ServiceLoader 来加载 具体的 实现类
?

我们最熟悉的就是mysql-connector驱动? Java定义了java.sql.Driver接口,然后数据库厂家来实现这套规范,msyql 、Oracle等等都去实现这个接口,就是使用的Java SPI机制

?但是JDK的SPI机制在某些方面又有些不足,引用官网的一句话:

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

2.Dubbo的SPI

约定:

在扩展类的 jar 包内,在资源路径META-INF/dubbo(或META-INF/services、META-INF/dubbo/internal)下放置扩展点配置文件,文件名称为接口全限定名且无后缀,文件内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

如果要定义dubbo SPI接口,需要在接口上添加@SPI注解,value为默认实现。
?

@SPI注解可以使用在类,接口和枚举上,Dubbo框架中都是使用在接口上。它的主要作用就是标记该接口是一个Dubbo SPI 接口,即是一个扩展点,可以有多个不同的内置实现或者自定义实现。运行时需要通过配置找到具体的实现类。(然而说了这么多,刚开始看还是不知道什么叫做扩展点,扩展点有什么作用。第一次我看到这我也是懵逼的,问题不大继续看)

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

    /**
     * default extension name
     * 我们可以看到这个value的默认值是一个空字符串,我们可以通过传递value值来指定这个接口的默认实现是什么, 比如Transporter  他的默认就是netty
     */
    String value() default "";

}

@Adaptive可以标记在类、接口、枚举和方法上,但是整个Dubbo中只有几个地方使用在类级别上,如AdaptiveExtensionFactory 和AdaptiveCompiler,其余都标注在方法上。如果标记在接口的方法上,即方法级别的注解,则可以通过参数动态的获得实现类,怎么动态的获取需要看getExtension方法。在后面我会说到这里

@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 examples, 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 appear either</li>
     * <li>otherwise, throw {@link IllegalStateException}</li>
     * </ol>
     * If default extension's name is not give on interface's {@link SPI}, then a 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 com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is
     * <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
     *
     * @return parameter key names in URL
     */
    String[] value() default {};

}

@Activate可以标注在类、接口、枚举类和方法上。主要使用在有多个扩展点的实现、需要根据不同条件被激活的场景中,如果Filter需要多个同时激活,因为每个Filter实现的是不同功能。@Activate可传入的参数很多

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    /**
     * Activate the current extension when one of the groups matches. The group passed into
     * {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
     *
     * @return group names to match
     * @see ExtensionLoader#getActivateExtension(URL, String, String)
     */
    String[] group() default {};

    /**
     * Activate the current extension when the specified keys appear in the URL's parameters.
     * <p>
     * For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
     * there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
     * </p>
     *
     * @return URL parameter keys
     * @see ExtensionLoader#getActivateExtension(URL, String)
     * @see ExtensionLoader#getActivateExtension(URL, String, String)
     */
    String[] value() default {};

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

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

    /**
     * Absolute ordering info, optional
     *
     * @return absolute ordering info
     */
    int order() default 0;
}

SPI的源码中最重要的一个类就是ExtensionLoader,我们只有拿到了这个扩展类加载器,那么才可以进行后面的操作

在看Dubbo的SPI的源码之前我们先写一个demo,看看怎么使用这个扩展机制。先学会使用再来研究源代码

我准备一个环境,一个接口两个实现类

/**
 * @author wsf
 * @since 20220224
 */
// 定义了一个接口 并且标注了该类是一个扩展点类,默认的扩展点就是@SPI的value值 car2
@SPI("car2")
public interface Car {

    void print();
}


/**
 * @author wsf
 * @since 20220224
 */
public class Car1 implements Car{

    @Override
    public void print() {
        System.out.println("car1....");
    }
}


/**
 * @author wsf
 * @since 20220224
 */
public class Car2 implements Car{

    @Override
    public void print() {
        System.out.println("car2....");
    }
}

2.加入Dubbo的SPI的配置

??????? 1.接口名字就是这个文件的名字

??????? 2.文件里面就是一个key-value的形式

?3.做一个测试类看下

/**
 * @author wsf
 * @since 20220224
 */
public class SPITest {

    public static void main(String[] args) {
        ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
        Car defaultExtension = extensionLoader.getDefaultExtension();
        defaultExtension.print();  // 输出  car2....


        //Car adaptiveExtension = extensionLoader.getAdaptiveExtension();
        //adaptiveExtension.print();
    }
}

?因为我们在SPI的注解上标注了car2是默认扩展类,所以这里输出了car2.... 这样就是一个简单的demo

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:04:04  更:2022-04-06 16:07:20 
 
开发: 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年11日历 -2024/11/24 4:36:27-

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