SPI技术理解及应用

一、什么是SPI ?

SPI 全称:Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。

面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移到了程序之外。

SPI的作用就是为被扩展的API寻找服务实现。

SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。

本质:Java SPI 实际上是“基于接口的编程+策略模式+约定配置文件” 组合实现的动态加载机制,在JDK中提供了工具类:“java.util.ServiceLoader”来实现服务查找。

SPI整体机制图如下:

当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。

二、SPI 的不足

1.不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。

2.获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。(Spring 的BeanFactory,ApplicationContext 就要高级一些了。)

3.多个并发多线程使用 ServiceLoader 类的实例是不安全的。

三、API 与 SPI

API代表应用程序编程接口,其中API是一种访问某种软件或平台提供的服务/功能的方法。
SPI代表服务提供商接口,其中SPI是注入,扩展或更改软件或平台行为的方式。

API通常是用作客户端访问服务,并且具有以下属性:
1、API是一种访问服务以实现特定行为或输出的编程方式。
2、但是API一旦被客户端使用,除非有适当的通信,否则它不能(也不应)被更改/删除。
SPI面向服务提供者,并且具有以下属性:
1、SPI是一种扩展/更改软件或平台行为的方式。
2、添加SPI接口将导致问题并可能破坏现有的实现。
换句话说,API会告诉您特定的类/方法为您执行什么操作,而SPI则告诉您必须执行哪些操作才能符合要求。

API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。

SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。

四、SPI 应用场景

SPI扩展机制应用场景有很多,比如Common-Logging,JDBC,Dubbo等等。

SPI流程:

  • 有关组织和公式定义接口标准
  • 第三方提供具体实现: 实现具体方法, 配置 META-INF/services/${interface_name} 文件
  • 开发者使用

比如JDBC场景下:

首先在Java中定义了接口java.sql.Driver,并没有具体的实现,具体的实现都是由不同厂商提供。
在MySQL的jar包mysql-connector-java-6.0.6.jar中,可以找到META-INF/services目录,该目录下会有一个名字为java.sql.Driver的文件,文件内容是com.mysql.cj.jdbc.Driver,这里面的内容就是针对Java中定义的接口的实现。
同样在PostgreSQL的jar包PostgreSQL-42.0.0.jar中,也可以找到同样的配置文件,文件内容是org.postgresql.Driver,这是PostgreSQL对Java的java.sql.Driver的实现。

个人简单描述下使用的过程(源码就不贴了):

  1. 建立链接程序提前执行ServiceLoader对java.sql.Driver的具体实现进行实例化。
  2. 具体的实例化中会获取当前服务的一些配置属性,封装完善该Driver具体实现类。
  3. 建立链接的方法使用到具体的Driver实现类建立链接。
五、ServiceLoader原理

服务端很好理解,就是一个定义,客户端来看看ServiceLoader的源码。

首先,ServiceLoader实现了Iterable接口,所以它有迭代器的属性,这里主要都是实现了迭代器的hasNext和next方法。这里主要都是调用的lookupIterator的相应hasNext和next方法,lookupIterator是懒加载迭代器。

其次,LazyIterator中的hasNext方法,静态变量PREFIX就是”META-INF/services/”目录,这也就是为什么需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件。

最后,通过反射方法Class.forName()加载类对象,并用newInstance方法将类实例化,并把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型) 然后返回实例对象。

六、代码实践案例

1、利用三方jar包来利用注解生成META-INFO/services, 注解:@MetaInfServices

<dependency>
    <groupId>org.kohsuke.metainf-services</groupId>
    <artifactId>metainf-services</artifactId>
</dependency>

定义客户端为interface工程,定义学习接口Study,定义方法为学习方式

定义服务端是provider工程,定义三个实现类为配置组件,加入注解 @MetaInfServices

编译之后target存在配置文件

2、直接使用框架的SPI机制(Spring SPI)
Spring SPI介绍:

  • 工具类:Spring中使用的类是SpringFactoriesLoader,在org.springframework.core.io.support包中
  • SpringFactoriesLoader 会扫描 classpath 中的 META-INF/spring.factories文件。
  • SpringFactoriesLoader 会加载并实例化 META-INF/spring.factories 中的指定类型。
  • META-INF/spring.factories 内容必须是 properties 的Key-Value形式,多值以逗号隔开。

使用细节:

  • spring.factories内容的key不只能是接口,也可以是抽象类、具体的类。但是有个原则:=后面必须是key的实现类(子类)
  • key还可以是注解,比如SpringBoot中的的key:org.springframework.boot.autoconfigure.EnableAutoConfiguration,它就是一个注解。
  • 文件的格式需要保证正确,否则会返回[](不会报错)
  • =右边必须不是抽象类,必须能够实例化。且有空的构造函数~
  • loadFactories依赖方法loadFactoryNames。loadFactoryNames方法只拿全类名,loadFactories拿到全类名后会立马实例化。
  • 此处特别注意:loadFactories实例化完成所有实例后,会调用AnnotationAwareOrderComparator.sort(result)排序,所以它是支持Ordered接口排序的,这个特点特别的重要。

更多精彩文章请访问我的个人博客(zhuoerhuobi.cn)

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TMS320F2837xD是德州仪器公司(TI)生产的一款数字信号处理器(DSP)。而创龙技术手册是针对该系列DSP的一份详细说明书。SPI(Serial Peripheral Interface)是一种串行外设接口,它能够在DSP与其他外设之间进行通信和数据交换。 TMS320F2837xD系列DSP的创龙技术手册中包含了关于SPI接口的详细信息。该手册介绍了SPI接口的特性、时序图和基本操作步骤等。此外,手册还提供了针对SPI接口的代码示例和应用场景,方便开发者们在实际项目中进行SPI接口的使用和应用。 在使用TMS320F2837xD系列DSP进行SPI通信时,可以根据手册中的指导,配置DSP的相应寄存器,设置通信模式、时钟频率和数据传输格式等参数。通过SPI接口,可以与其他外设,如传感器、存储器、显示器等进行数据交换与通信,实现设备之间的互联互通。 同时,创龙技术手册还提供了关于SPI接口的错误处理和故障排除的信息,帮助开发者们在出现问题时进行诊断和修复。 总之,TMS320F2837xD创龙技术手册SPI部分提供了丰富的信息和指导,方便开发者们在使用该系列DSP进行SPI通信时,能够轻松地配置、操作和调试SPI接口,实现与其他外设之间的高效数据交换和通信。 ### 回答2: TMS320F2837xD是德州仪器(Texas Instruments)推出的一款高性能数字信号处理器(DSP)芯片。它具有丰富的功能和灵活的配置选项,适用于各种应用领域,包括工业控制、汽车电子、医疗设备等。 在TMS320F2837xD的创龙技术手册中,SPI(Serial Peripheral Interface)是其中一个重要的通信接口。SPI是一种同步的串行通信协议,用于在芯片之间传输数据。它通常由一个主设备(Master)和一个或多个从设备(Slave)组成。 SPI接口有四根线,分别是SCLK(时钟线)、MOSI(主设备输出、从设备输入线)、MISO(主设备输入、从设备输出线)和SS/CS(片选线)。在系统中,主设备通过控制时钟线向从设备发送时钟信号,并通过MOSI线发送数据,从设备则通过MISO线返回响应数据。 SPI接口可以灵活配置,可以使用不同的数据位宽、时钟极性和相位等参数进行通信。在TMS320F2837xD中,SPI接口可以通过设置SPI控制寄存器来配置和控制。 创龙技术手册中提供了SPI接口的详细说明,包括时序图、寄存器配置和使用示例。用户可以根据需要设置相关寄存器,初始化SPI接口,并使用相应的函数和指令进行数据的收发。 总而言之,TMS320F2837xD创龙技术手册中的SPI部分提供了有关SPI接口的详细信息,帮助用户理解和使用SPI接口实现芯片间的串行通信。通过合理的配置和控制,可以实现高效、可靠的数据传输。 ### 回答3: TMS320F2837xD是德州仪器(TI)推出的一款高性能数字信号处理器(DSP),用于嵌入式系统中的应用。而SPI(Serial Peripheral Interface)是一种串行外设接口,常用于各种嵌入式设备之间的通信。 在TMS320F2837xD的创龙技术手册中,关于SPI的内容主要包括SPI的基本原理、工作模式、时序、寄存器配置等方面。 首先,手册会介绍SPI的基本原理,说明SPI是如何实现数据的传输和通信的。SPI是一种同步的串行通信协议,通过主设备(通常为DSP)和从设备之间的交互来实现数据传输。 手册中还会介绍SPI的工作模式,包括主模式和从模式。主模式下,DSP作为主设备发送控制信号和数据;从模式下,DSP作为从设备接收控制信号和数据。 接着,手册会详细介绍SPI通信时的时序要求。SPI通信中,时钟信号的频率和边沿类型会对数据的传输速率和有效性产生影响,因此手册会提供具体的时序要求,以确保通信的稳定性和正确性。 最后,手册会介绍TMS320F2837xD上与SPI相关的寄存器配置。通过对寄存器的设置,可以对SPI的工作模式、时钟频率等进行配置,以满足具体应用的需求。 总之,TMS320F2837xD的创龙技术手册中关于SPI的内容会详细介绍SPI的基本原理、工作模式、时序要求以及寄存器配置等方面的知识,帮助开发人员在嵌入式系统中正确使用和配置SPI接口实现设备间的高效通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值