SPI原理

SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java spi机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不使用实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候动态指定具体实现类,这就需要一种服务发现机制。 java spi就是提供这种功能的机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

java SPI应用场景很广泛,在Java底层和一些框架中都很常用,比如java数据驱动加载和Dubbo。Java底层定义加载接口后,由不同的厂商提供驱动加载的实现方式,当我们需要加载不同的数据库的时候,只需要替换数据库对应的驱动加载jar包,就可以进行使用。

要使用Java SPI,需要遵循如下约定:

  • 1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;

  • 2、接口实现类所在的jar包放在主程序的classpath中;

  • 3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;

  • 4、SPI的实现类必须携带一个不带参数的构造方法;

SPI实现

1. 建立maven工程

(1)spi-demo-api 提供需要实现的接口

public interface SpiDemo {
void say();
}

(2)spi-demo-impl1 为第一种实现

public class SpiDemoImpl1 implements SpiDemo {
public void say() {
System.out.println("SpiDemoImpl1");
}
}

每一个SPI接口都需要在自己项目的静态资源目录中声明一个services文件,文件名为实现规范接口的类名全路径。在resources目录中创建\META-INF\services目录,创建以com.hanggle.spi.api.SpiDemo为名的文件。(文件名即是要实现的接口类的全路径如下图)

文件内容:

com.hanggle.spi.api.impl1.SpiDemoImpl1

(3)spi-demo-impl2 为第二种实现

public class SpiDemoImpl2 implements SpiDemo {
public void say() {
System.out.println("SpiDemoImpl2");
}
}

同spi-demo-impl1一样

在resources目录中创建\META-INF\services目录,创建以com.hanggle.spi.api.SpiDemo为名的文件。

文件内容:

com.hanggle.spi.api.impl1.SpiDemoImpl2

(4)spi-demo-main 程序中的使用

public static void main(String[] args) {
ServiceLoader<SpiDemo> serviceLoader = ServiceLoader.load(SpiDemo.class);
for (SpiDemo o : serviceLoader) {
o.say();
}
}

运行结果:

SPI 源码

装配文件路径的定义:

有源代码可以,java会根据定义的路径去扫描可能存在的接口的实现。放在config中,然后使用parse方法将配置文件中的接口实现全路径放在pending中,并取得第一个实现类(变量nextName),

然后使用类加载器加载,加载需要调用的类,然后调用实现的方法

优缺点

优点:

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

缺点:

  • 虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。

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

参考:http://www.pandan.xyz/2017/03/14/java%20SPI%E6%9C%BA%E5%88%B6%E5%8E%9F%E7%90%86/

https://yq.aliyun.com/articles/640161

SPI 实现原理及运用的更多相关文章

  1. 了解一下Java SPI的原理

    了解一下Java SPI的原理 1 为什么写这篇文章? 近期,本人在学习dubbo相关的知识,但是在dubbo官网中有提到Java的 SPI,这个名词之前未接触过,所以就去看了看,感觉还是有很多地方有 ...

  2. 嵌入式物联网之SPI接口原理与配置

    本实验采用W25Q64芯片 W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为64Mb.该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件.W25Q64将8M字节的容量分为 ...

  3. 关于SPI通信原理与程序实现

    第一次接触SPI是因为当时用到NRF24L01,需要用SPI进行通信.因为2401上面写着MOSI.MISO.SS.RST,当时以为只要用到SPI就肯定有这几个引脚,以至于限制了自己的思维.只认识MI ...

  4. [转]SPI通信原理简介

    [转自]http://www.cnblogs.com/deng-tao/p/6004280.html 1.前言 SPI是串行外设接口(Serial Peripheral Interface)的缩写.是 ...

  5. Java SPI机制原理和使用场景

    SPI的全名为Service Provider Interface.这个是针对厂商或者插件的.一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块,往往有很多不同的实现 ...

  6. Dubbo学习笔记6:Dubbo增强SPI与SPI中扩展点自动包装的实现原理

    在Dubbo整体架构分析中介绍了Dubbo中除了Service和Config层为API外,其他各层均为SPI,为SPI意味着下面各层都是组件化可以被替换的,也就是扩展性比较强,这也是Dubbo比较好的 ...

  7. Dubbo——SPI及自适应扩展原理

    文章目录 引言 正文 一.什么是SPI? 1. Java SPI的实现 2. Dubbo SPI实现原理 由配置文件得到的猜想 SPI源码 二.自适应扩展机制 三.Dubbo IOC 总结 引言 Du ...

  8. Java中的SPI原理浅谈

    在面向对象的程序设计中,模块之间交互采用接口编程,通常情况下调用方不需要知道被调用方的内部实现细节,因为一旦涉及到了具体实现,如果需要换一种实现就需要修改代码,这违反了程序设计的"开闭原则& ...

  9. 【转】SPI总线协议

    SPI总线协议 By Xiaomin | April 17, 2016| 技术 概述 SPI(Serial Peripheral Interface)总线是主要应用于嵌入式系统内部通信的串行同步传输总 ...

随机推荐

  1. C语言学习笔记--#和##操作符

    1. #运算符 (1)#运算符用于在预处理期将宏的参数转换为字符串 (2)#的转换作用是在预处理期完成的,因此只在宏定义中有效,即其他地方不能用#运算符 (3)用法:#define STRING(x) ...

  2. python的程序书写以及保存

    在前面的微博里已经叙述Python的安装以及路径的设置. 在我们的windows的电脑上面我们已经安装完了python以及设置了它的路径,所以我们可以再cmd里面进行Python的编写(cmd的快速打 ...

  3. Jsonp实现跨域请求Ajax

    客户端 #!/usr/bin/env python import tornado.ioloop import tornado.web class MainHandler(tornado.web.Req ...

  4. JavaWeb 没用

    Servlet的生命周期 初始化:Web容器加载servlet,调用innit(),只执行一次 处理业务: 请求到达时,运行service方法 并调用相应的doget或者dopost方法.  可执行多 ...

  5. Python 黑客 004 用Python构建一个SSH僵尸网络 01 简介

    用Python构建一个SSH僵尸网络 01 简介 一. 构建一个SSH僵尸网络的流程图: Created with Raphaël 2.1.0手动操作,实现通过SSH连接目标服务器(手动)用 Pexp ...

  6. DPDK内存管理-----rte_mbuf(转)

    本文主要介绍rte_mbuf与rte_mempool数据结构之间的组织关系.以及网卡接收到的数据是如何存储在rte_mbuf中的. 一.rte_mbuf.rte_mempool及网卡收到的数据包在内存 ...

  7. g2o20160424 CMakeLists.txt

    LIB_PREFIX: 设置生成库的前缀 SET(LIB_PREFIX g2o_) # The library prefix SET(LIB_PREFIX g2o_) 变量的默认配置 # defaul ...

  8. 基于GStreamer编写Mp3播放器

    一.简介 作者系统为CentOS6,本文在此基础上对Mp3播放器进行开发,需要使用mp3解码库libmad和gstreamer0.10-plugins-ugly,详细步骤如下.   二.操作步骤 1) ...

  9. loj2395 [JOISC 2017 Day 2]火车旅行

    传送门 分析 我们知道无论往左走还是往右走一定都是往不低于这个点的地方走 于是我们可以考虑用倍增来维护一个点向左和向右走$2^i$最远分别能走到哪里 我们可以先用单调栈求出直走一步的情况,之后再处理倍 ...

  10. 去掉utf-8的Bom头:使用java以及jdbc不使用第三方库执行sql文件脚本

    package com.xxx.xxx.dao; import java.io.BufferedReader; import java.io.File; import java.io.FileInpu ...