什么是 SPI

SPI是Service Provider Interface的简称,是JDK默认提供的一种将接口和实现类进行分离的机制。这种机制能将接口和实现进行解耦,大大提升系统的可扩展性。

SPI机制约定:当一个Jar包需要提供一个接口的实现类时,这个Jar包需要在META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该Jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

比如下面的列子,jcl-over-slf4j这个Jar包提供了conmon-logging中LogFactory这个接口的实现。

文件中的内容如下:

# 这里表名具体的实现类是`org.apache.commons.logging.impl.SLF4JLogFactory`这个类
org.apache.commons.logging.impl.SLF4JLogFactory # Axis gets at JCL through its own mechanism as defined by Commons Discovery, which
# in turn follows the instructions found at:
# http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service Provider

JDK为了方便查找服务的实现,还提供了一个工具类:java.util.ServiceLoader。

ServiceLoader<Object> loader = ServiceLoader.load(LogFactory);
loader.forEach((item)->{
System.out.println(item);
});

上面代码中使用ServiceLoader遍历使用SPI机制提供的所有LogFactory实现。

应用场景

SPI机制的主要应用有框架扩展和组件的替换等,比如

  • JDBC接口实现类的运行时加载:我们连接具体的数据库是都需要添加相关的Jar包依赖,但是不需要我们再做任何其他配置,只要将Jar包放到classpath下就行了。这是一个最常见的SPI应用场景。
  • 日志门面加载具体的日志实现类:之前的博客中介绍到,jcl和slf4j等只是日志实现类,Log4j和LOgBack才是具体的日志实现。JCL和SLF4J加载日志实现类时也使用了SPI机制,具体请看上面章节中举的列子。
  • Spring中大量使用了SPI:比如对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等

自己实现

下面就一步步从定义接口到提供SPI实现类来演示下SPI机制具体的使用方式。

step1:先定义一个接口

public interface SaySomething {

    String say(String name);

}

step2:编写实现类

public class ASaySomething implements SaySomething {
@Override
public String say(String name) {
return "Hi,"+name+", l am A...";
}
}

step3:在resource下添加META-INFO/services目录

添加完这个目录后,添加一个以SaySomething接口的全限定名为名字的文件,这个文件的内容是你要设置的具体实现类。这边我们就设置实现类为上面的ASaySomething

step4:使用SPI机制

public static void main(String[] args) {
ServiceLoader<SaySomething> loader = ServiceLoader.load(SaySomething.class);
loader.forEach(item ->{item.say("csx");});
}

API和SPI的比较

在开发中我们还经常会提到API这个名词,下面也总结下两者的区别:

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

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

优缺点

优点

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

缺点

  • SPI必须先将接口的所有实现类都遍历出来才能最后选择具体使用哪个类。有些不要的类也会被实例化,可能会比较浪费内存。
  • ServiceLoader 并不是线程安全的。

参考

作为一个Java工程师,你应该要知道SPI机制的更多相关文章

  1. 一个Java工程师的入门级Linux命令集

    0.前言    网上介绍linux的命令的文章一大堆,但是大部分都是流于命令介绍,把命令的所有参数都介绍一遍,但是其实在真正的工作中,很多参数都不会用到.本文总结了我自己常用的一些命令,这些命令都比较 ...

  2. Java是如何实现自己的SPI机制的? JDK源码(一)

    注:该源码分析对应JDK版本为1.8 1 引言 这是[源码笔记]的JDK源码解读的第一篇文章,本篇我们来探究Java的SPI机制的相关源码. 2 什么是SPI机制 那么,什么是SPI机制呢? SPI是 ...

  3. 一文搞懂Java/Spring/Dubbo框架中的SPI机制

    几天前和一位前辈聊起了Spring技术,大佬突然说了SPI,作为一个熟练使用Spring的民工,心中一紧,咱也不敢说不懂,而是在聊完之后赶紧打开了浏览器,开始的学习之路,所以也就有了这篇文章.废话不多 ...

  4. Java工程师如何在1个月内做好面试准备?

    作者:石杉的架构笔记 写在前面 春节长假转眼已过,即将迎来的是一年一度的金三银四跳槽季. 假如你准备在金三银四跳槽的话,那么作为一个Java工程师,应该如何利用1个月的时间,快速的为即将到来的面试进行 ...

  5. 【金三银四跳槽季】Java工程师如何在1个月内做好面试准备?

    目录 一.写在前面 二.技术广度的快速准备 三.技术深度的快速准备 四.基础功底的快速准备 五.下篇预告 一.写在前面 春节长假转眼已过,即将迎来的是一年一度的金三银四跳槽季. 假如你准备在金三银四跳 ...

  6. Java工程师核心书单推荐

    随便打开一个招聘网站,看看对高级Java工程师的技能要求. 抛开其它的经验能力等等,单纯从技术,或者说知识上来讲,可以发现一些共通的地方. Java基础 计算机基础 数据库,SQL/NoSQL 常用开 ...

  7. Java 工程师的学习线路图。

    今天了一个超级好用的工具,思维导图 FreeMind,于是顺道试用了一下,照着画了一张 Java 工程师的学习线路图.

  8. 一个java高级工程师的进阶之路

    宏观方面 一. JAVA.要想成为JAVA(高级)工程师肯定要学习JAVA.一般的程序员或许只需知道一些JAVA的语法结构就可以应付了.但要成为JAVA(高级) 工程师,您要对JAVA做比较深入的研究 ...

  9. 【推荐】Java工程师如何从普通成为大神值得一读

    本文源自 http://www.hollischuang.com/archives/489 一点感悟 java作为一门编程语言,在各类编程语言中作为弄潮儿始终排在前三的位置,这充分肯定了java语言的 ...

随机推荐

  1. Android布局属性与常用控件

    一.Android常用布局属性 1. LinearLayout的特有属性 android:orientation:设置布局排列方式   android:layout_weight:设置所占布局的权重  ...

  2. 「 从0到1学习微服务SpringCloud 」10 服务网关Zuul

    系列文章(更新ing): 「 从0到1学习微服务SpringCloud 」06 统一配置中心Spring Cloud Config 「 从0到1学习微服务SpringCloud 」07 RabbitM ...

  3. python3操作PyMySQL笔记

    python3操作mysql需要先安装PyMySQL pip install PyMySQL 在linux登录mysql ,并且在安装数据库时设置了数据库的用户名“root”和密码“root”,mys ...

  4. 2、初始ES6及Vue

    今日内容 es6的语法 let 特点: 1.局部作用域 2.不会存在变量提升 3.变量不能重复声明 const 特点: 1.局部作用域 2.不会存在变量提升 3.不能重复声明,只声明常量 不可变的量 ...

  5. STM8 关闭PWM输出后的电平输出问题解决

    STM系列的单片机PWM输出如果被关断比如用TIM1_CtrlPWMOutputs进行停止输出后,电平的高低处于不确定的状态. 他取决于: 1.GPIO初始化的特性 2.关断那一刻时的电平 3.CCM ...

  6. Spring 框架初学

    一 .   spring IOC, DI: 将原来有程序员建立类的对象工作交由spring来操作: 从外部来看: 将各个类以bean 的方式配在spring容器的配置文件中(application.x ...

  7. SpringCloud与微服务系列专栏

    一. 前置知识 学习SpringCloud之前需要具备和掌握如下框架和工具的使用:SpringMVC,Spring,Spring Boot,Mybatis,Maven,Git. SpringCloud ...

  8. 马尔科夫随机场(MRF)及其在图像降噪中的matlab实现

    (Markov Random Field)马尔科夫随机场,本质上是一种概率无向图模型 下面从概率图模型说起,主要参考PR&ML 第八章 Graphical Model (图模型) 定义:A g ...

  9. Idea-LifecycleException when deploying

    案例 今天第一次用idea构建项目,出现了如下问题: FAIL - Application at context path /myWebApp could not be started FAIL - ...

  10. Linux文件和目录权限实战讲解

    一 相关课程回顾1.1 linux文件类型当执行ls -l或ls -la 命令后显示的结果中最前面的第2~10个字符是用来表示文件权限 第一个字符一般用来区分文件和目录: d:表示是一个目录,事实上在 ...