iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >javaspi最全使用总结
  • 440
分享到

javaspi最全使用总结

2024-04-02 19:04:59 440人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录前言一、jdk中SPI的使用规范案例展示SPI优点SPI缺点SPI机制在实际生产中的一个应用二、dubbo 中SPI的使用Dubbo 的 SPI 举例三、SpringBoot 中

前言

开发过程中,经常要用到第三方提供的SDK来完成一些业务扩展功能,比如调用第三方的发短信、图片验证码、人脸识别等等功能,但问题是,第三方SDK只是提供了标准的功能实现,某些场景下,开发者还想基于这些SDK做一些个性化的定制和扩展,那要怎么办呢?

于是,一些优秀的SDK就通过SPI的机制,将一些接口扩能开放出来,开发者就可以基于这些SPI接口做自身的业务扩展了;

总结一下SPI思想:在系统的各个模块中,往往有不同的实现方案,例如日志模块的方案、xml解析的方案等,为了在装载模块的时候不具体指明实现类,我们需要一种服务发现机制,java spi就提供这样一种机制。有点类似于ioc的思想,将服务装配的控制权移到程序之外,在模块化设计时尤其重要

Java 的SPI机制在很多框架中间件等都有着广泛的使用,如springboot,Dubbo中均有采用,属于高级Java开发知识点,有必要掌握

下面用一张简图说明下SPI机制的原理

一、JDK中SPI的使用规范

  • 定义通用的服务接口,针对服务接口,提供具体实现类
  • jar包的META-INF/services/目录中,新建一个文件,文件名为 接口的 “全限定名”, 文件内容为该接口的具体实现类的 “全限定名”
  • 将spi所在jar放在主程序的classpath中
  • 服务调用方用java.util.ServiceLoader,用服务接口为参数,去动态加载具体的实现类到JVM

案例展示

案例业务背景:

  • 提供一个统一的支付接口
  • 有两种支付方式,分别为支付宝支付和微信支付,实际中为不同支付厂商提供的SDK
  • 客户端为customer工程,即调用支付SDK的使用者

从工程的结构来看,也是遵循SPI的服务规范的,即在resources目录下,创建一个指定名称的文件夹,将接口实现的全限定名放进去,那么客户端只需要依赖特定的SDK,然后通过 serviceLoader的方式即可加载到依赖的SDK的服务

客户端customer工程导入依赖


	<dependencies>

        <dependency>
            <artifactId>service-common</artifactId>
            <groupId>com.congge</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <artifactId>ali-pay</artifactId>
            <groupId>com.congge</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <artifactId>wechat-pay</artifactId>
            <groupId>com.congge</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

public class MainTest {

    public static void main(String[] args) {

        ServiceLoader<PayService> loader = ServiceLoader.load(PayService.class);
        loader.forEach(payService ->{
            System.out.println(payService);
            payService.pay();
            System.out.println("=======");
        });
    }

}

运行下这段客户端的测试程序

我们不妨来看看serviceLoader中的一段关键代码,即加载服务接口时,可以发现,该方法最终要去找接口的实现类所在jar包下的 “META-INF/services” 目录中的服务实现,如果找到了就能被加载和使用

SPI优点

  • 使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离
  • 应用程序可根据实际业务情况启用框架扩展或替换框架组件

SPI缺点

  • srviceLoader 只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍
  • 如果并不想用某些实现类,它也被加载并实例化了,这就造成了浪费
  • 获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类
  • 多个并发多线程使用ServiceLoader类的实例是不安全的,需要加控制

SPI机制在实际生产中的一个应用

在小编的实际项目开发中,有这样一个需求,标准产品针对单点登录提供了多种实现,比如 基于cas方案,ldap方案,oauth2.0方案等,针对每种方案,提供了一套具体的实现,即封装成了各自的jar包

标准产品在出厂并在客户端安装的时候,会有一套默认的实现,即oauth2.0实现,但是客户方有时候有自己的一套,比如cas服务器,那么客户希望能够对接cas单点登录,这么以来,具体到项目在实际部署的时候,就需要现场做一些特定的参数配置,将标准实现切换为 cas的实现即可,那么问题来了,标准产品是如何根据参数配置做到的呢?

其实也很简单,就是使用了 serviceLoader机制,自动发现标准产品中能够加载到的所有单点登录实现,如果没有外部配置参数传入,则默认使用oauth2.0的实现,否则,将会采用外部参数传过来的那个实现。

二、DUbbo 中SPI的使用

可以说,dubbo框架是对spi使用的一个很好的例子,dubbo框架本身就是基于SPI规范做了更进一步的封装,从上面的优缺点分析中,我妈了解了原生的SPI在客户端选择服务的时候需要遍历所有的接口实现,比较浪费资源,而dubbo在此基础上有了更好的封装和实现,下面来了解下dubbo的SPI使用吧

Dubbo 的 SPI 规范是:

接口名:可随意定义

实现类名:在接口名前添加一个用于表示自身功能的“标识前辍”字符串

提供者配置文件路径:在依次查找的目录为

  • META-INF/dubbo/internal
  • META-INF/dubbo
  • META-INF/services

提供者配置文件名称:接口的全限定性类名,无需扩展名

提供者配置文件内容:文件的内容为 key=value 形式,value 为该接口的实现类的全限类性类名,key 可以随意,但一般为该实现类的“标识前辍”(首字母小写)。一个类名占 一行

提供者加载:ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader 类,用于加载提供者配置文件中所有的实现类,并创建相应的实例

Dubbo 的 SPI 举例

1、创建一个Maven工程,并导入核心依赖


		<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

2、 定义 SPI 接口

比如这里有一个下单的接口,注意接口上需要加上@SPI 注解 ,注解里面的值可以填,也可以不用填,如果填,请注意和配置文件里面的key值名称保持一致,填写了的话,加载的时候,会默认找这个key对应的实现


@SPI("alipay")
public interface Order {
    String way();
}

3、定义两个接口的实现类


public class AlipayOrder implements Order{

    public String way() {
        System.out.println("使用支付宝支付");
        return "支付宝支付";
    }

}

public class WechatOrder implements Order {

    public String way() {
        System.out.println("微信支付");
        return "微信支付";
    }

}

4、定义扩展类配置文件


alipay=com.congge.spi.AlipayOrder
wechat=com.congge.spi.WechatOrder

5、测试方法


	@Test
    public void test1(){
        ExtensionLoader<Order> extensionLoader = ExtensionLoader.getExtensionLoader(Order.class);
        Order alipay = extensionLoader.getExtension("alipay");
        System.out.println(alipay.way());

        Order wechat = extensionLoader.getExtension("wechat");
        System.out.println(wechat.way());
    }

如果不指定加载哪个,而且接口配置了默认值,这里只需要在getExtension中设置 “true”,就会自动加载默认的那个

在Dubbo源码中,很多地方会存在下面这样的三种代码,分别是自适应扩展点、指定名称的扩展点、激活扩展点,dubbo通过这些扩展的spi接口实现众多的插拔式功能


ExtensionLoader.getExtensionLoader(xxx.class).getAdaptiveExtension();
ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name);
ExtensionLoader.getExtensionLoader(xxx.class).getActivateExtension(url, key);

以dubbo源码中的Protocol 为例,对应dubbo源码中的rpc模块


@SPI("dubbo")  
public interface Protocol {  
      
    int getDefaultPort();  
  
    @Adaptive  
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;  
  
    @Adaptive  
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;  

    void destroy();  
 
} 


Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
  • Protocol接口,在运行的时候dubbo会判断一下应该选用这个Protocol接口的哪个实现类来实例化对象
  • 如果你配置了Protocol,则会将你配置的Protocol实现类加载到JVM中来,然后实例化对象时,就用你配置的那个Protocol实现类就可以了

上面那行代码就是dubbo里面大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态的根据配置去找到对应的实现类。如果你没有配置,那就走默认的实现类,即dubbo

三、springboot 中SPI思想的使用

我们知道,springboot框架相比spring,从配置文件上简化了不少,但简化的只是开发者看到的那些xml配置文件中的东西,其本质仍然未变,就算是少了xml配置文件,依旧在启动的时候,需要做配置的解析工作,如解析原来的数据库连接的xml配置文件中的内容加载到spring容器

而springboot来说,很多看不到的配置文件,都是在容器启动过程中,自动将配置进行读取,解析和加载,而在这个过程中,我们不禁好奇,这些配置是存在哪里呢?这里就用到了SPI的思想,也就是涉及到springboot的自动装配过程

举例来说,springboot怎么知道启动时需要加载 DataSource这个数据库连接的bean对象呢?怎么知道要使用JdbcTemplate 还是Druid的连接呢?

在spingboot工程启动过程中,有很重要的一个工作,就是完成bean的自动装配过程,自动装配装配的是什么东西呢?简单来说就是:

  • 扫描classpath(工程目录下)下所有依赖的jar包装中指定目录中以特定的全限定名称的文件,进行解析并装配成bean
  • 扫描xml文件,解析xml配置并装配成bean
  • 解析那些被认为是需要装配的配置类,如@configuration,@service等

其中第一步中的那些文件是什么呢?其实就是和dubbo或原生的spi规范中的那些 /META-INF 文件,只不过在springboot工程中,命名的格式和规范稍有不同

下面通过源码来看看springboot启动过程中是如何加载这些spi文件的吧

然后来到下面这里,重点关注setInitializers 这个方法,顾名思义,表示在启动过程中要做的一些初始化设置,那么要设置哪些东西呢?

在这个方法中,有一个方法getSpringFactoriesInstances,紧接着这个方法看进去

在该方法中需要重点关注这句代码,通过这句代码,将依赖包下的那些待装配的文件进行加载,说白了,就是加载classpath下的那些 spring.factory的文件里面的name


SpringFactoriesLoader.loadFactoryNames(type, classLoader)

那么问题是具体加载的是什么样的文件呢?不妨继续点进去看看,在SpringFactoriesLoader类的开头,有一个这样的路径,想必大家就猜到是什么了吧

也就是说,会去找以这样的名字结尾的文件,比如我们在依赖的jar包中,看到下面这一幕,在这个spring.factories中,会看到更多我们熟悉的配置

这样问题就很明白了,通过找到spring.factories的文件,然后解析出具体的类的完整的名称,然后再在:createSpringFactoriesInstances 这个方法中完成对这些 扩展的SPI接口实现类的初始化加载,即完成配的过程

沿着这个思路继续探究下去,相信感兴趣的同学对springboot中的这种类SPI的方式会有更深一层的理解,本篇到此结束,最后感谢观看!

到此这篇关于java spi最全使用总结的文章就介绍到这了,更多相关java spi详解内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: javaspi最全使用总结

本文链接: https://www.lsjlt.com/news/140164.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • javaspi最全使用总结
    目录前言一、JDK中SPI的使用规范案例展示SPI优点SPI缺点SPI机制在实际生产中的一个应用二、DUbbo 中SPI的使用Dubbo 的 SPI 举例三、springboot 中...
    99+
    2024-04-02
  • 2023最新总结,Mac下使用Homebrew完全指南!
    1. 介绍 Homebrew是一款包管理工具,目前支持macOS和Linux系统。主要有四个部分组成:brew、homebrew-core 、homebrew-cask、homebrew-bottles。 ...
    99+
    2023-09-14
    个人开发 macos 经验分享 前端 Powered by 金山文档
  • 最全总结SpringBean的作用域管理
    目录一、前言1.1 详细介绍下 singleton 作用域?1.2 和单例模式有何联系去区别呢?1.3 Prototype作用域二、bean的作用域三、singleton —— 唯一...
    99+
    2024-04-02
  • Java泛型最全知识总结
    目录一、泛型简介1.1 泛型的概念 1.2 泛型的引入背景1.3 引入泛型的目的二、泛型在集合中的应用2.1 在集合中没有使用泛型的例子2.2 在集合中使用泛型的例子12.3 在集合...
    99+
    2024-04-02
  • android中adb命令最全总结
    目录 一、查看adb版本 二、查看已经连接的设备 三、获取手机序列号 四、查看手机设别型号 五、查看手机分辨率 六、获取手机的mac地址 七、查看日志 八、查看电池信息 九...
    99+
    2024-04-02
  • Java同步锁synchronized用法的最全总结
    目录一、并发同步问题二、锁的简介三、synchronized的三种应用方式1.修饰一个实例方法2.修饰一个静态方法3.修饰一个代码块(2)锁对象为类的Class对象四、synchro...
    99+
    2023-03-21
    Java同步锁 synchronized的总结
  • java: 找不到符号 最全总结
    一、业务场景 在编译器里代码正常编译,但是在进行maven打包是报错:java: 找不到符号 二、解决方法: 1、清除编码工具缓存,需要重启会重启编译器,我用的是idea,Eclips...
    99+
    2023-09-27
    maven intellij-idea java Powered by 金山文档
  • Python数据类型最全知识总结
    目录一、什么是数据类型二、字符串类型三、列表类型四、元组类型五、字典类型六、集合类型七、五种数据类型所支持的运算符比较八、数据的序列化和反序列化一、什么是数据类型 其实可以明白数据类...
    99+
    2024-04-02
  • 最全总结---36种MySQL时间函数
    1,NOW():返回当前日期和时间 SELECT NOW();//这将返回当前日期和时间的值,例如:2023-03-11 15:32:45。 2,DATE():从日期时间值中提取日期部分: SELECT DATE('2023-03-...
    99+
    2023-08-18
    mysql unix 数据库
  • 最全CTF Web题思路总结(更新ing)
    网上似乎没有一篇比较完整的CTF Web题思路的总结,希望这篇“最全总结”对各位师傅有帮助。 基础 Flag可能出现的位置 网页源代码(注意注释) 数据库中 phpinfo 靶机中的文件 环境变...
    99+
    2023-09-02
    网络安全 php web
  • sqlite3使用总结
           OS X自从10.4后把SQLite这套相当出名的数据库软件,放进了作业系统工具集里。OS X包装的是第三版的SQLite,又称SQLite3。这套软件有几个特色:软件属于公共财(public domain),SQLite可说...
    99+
    2023-01-31
  • clipboard.js使用总结
    目录(1)介绍: (2)clipboard复印内容的方式有 (3)Function操作有两种: (4)通过属性返回复印的内容 (5)函数和属性的兼容方式 (1)介绍: clipbo...
    99+
    2024-04-02
  • BigDecimal使用总结
    对于超过16位的大型数字,需要用到Java在java.math包中提供的API类BigDecimal,而且也不是传统的+-*/,而是调用对应的方法。 1.创建对象 使用new的方式创建BigDecim...
    99+
    2023-09-03
    java BigDecimal
  • redis 使用总结
    最近一段时间与redis接触比较频繁。发现有些东西还是工作中经常会用到的,自己也花了点时间巩固下。本篇文章主要是以总结性的方式梳理,因为redis的主题很大,任何一个技术点展开都是几篇文章的量。也可以说这篇...
    99+
    2024-04-02
  • w3m使用总结
    ...
    99+
    2023-01-31
    w3m
  • JAVASPI机制详解使用方法
    目录写在前面什么是SPI使用场景实现约定四种角色基于JAVA原生特性实现的JAVA SPI机制的DEMO1. 主要角色2. 示例代码3. 说明基于SPRING BOOT实现的JAVA...
    99+
    2024-04-02
  • 全网最全Android Framework框架总结,Android如何入门Framework层
    每一个Android开发,基本都了解或者学习过系统的知识,一是因为国内软件行业内卷,找工作时“面试造火箭,工作拧螺丝”的局面导致的,另一方面,从客观角度来讲,学习Android系统、Framework...
    99+
    2023-08-31
    android android studio ide
  • Java创建线程的七种方法,全网最全面总结~
    目录 前言 一、继承Thread,重写run方法 二、实现Runnable接口,重写run方法 三、使用匿名内部类创建 Thread 子类对象 四、使用匿名内部类,实现Runnable接口 五、lambda表达式 六、实现Callable...
    99+
    2023-09-13
    java 多线程
  • Python最常用的20个包总结
    numpy(数据处理和科学计算) 代码示例: arr = np.array([1, 2, 3, 4, 5]) print(arr) pandas(数据处理和分析) 代码示例: ...
    99+
    2023-05-15
    Python常用 Python常用包
  • Java ArrayList使用总结
    目录类图源码解析新增和扩容实现扩容的本质删除总结提起ArrayList,相信很多小伙伴都用过,而且还不少用。但在几年之前,我在一场面试中,面试官要求说出ArrayList的扩容机制。...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作