什么是 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. java 方法定义 调用

    一.定义 格式: 修饰符 返回值类型 方法名(参数){ return } 相比之下python方法的定义简单多了 public static 是修饰符 二.调用 方法名(); 注意:要在main方法中 ...

  2. XSS Cheat Sheet

    Basic and advanced exploits for XSS proofs and attacks. Work in progress, bookmark it. Technique Vec ...

  3. Paper慢慢读 - AB实验人群定向 Double Machine Learning

    Hetergeneous Treatment Effect旨在量化实验对不同人群的差异影响,进而通过人群定向/数值策略的方式进行差异化实验,或者对实验进行调整.Double Machine Learn ...

  4. 如何优雅的将Mybatis日志中的Preparing与Parameters转换为可执行SQL

    原文链接 疫情期间大家宅在家里是不是已经快憋出“病”了~~ ​ 公司给开了VPN,手机电脑都能连,手机装上APP测试包,就能干活了,所以walking从2020.02.01入京以来,已经窝在家里11天 ...

  5. Android 之采用execSQL和rawQuery方法完成数据的添删改查操作

    使用 SQLiteDatabase 操作 SQLite 数据库 [java] view plaincopy /* Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库 ...

  6. web前端sprite,精灵图,雪碧图

    css sprite 俗称:精灵图,雪碧图,指将整个页面不同的图片or图标合并在一张图上:优点:使用CSS Sprite 可以减少网络请求,提高网页加载性能,不会出现网页上端加载完毕下面还在加载中这一 ...

  7. 使用Java封装一个DBUtils类(反射)

    刚开始学JavaWeb时,我是调用N个setter方法将从数据库中查询出的数据封装成JavaBean的,极其繁琐. 后来了解SpringJDBC后,发现它提供的接口非常简单,然后就想自己封装一个简单的 ...

  8. 【限时免费】近1000G JAVA学习视频下载

    2020的情人节是个极特殊的情人节,面对肆虐的疫情,我们无法出门,宅在家里,也无法阻止你作为一名优秀程序员的梦想. 或许没有鲜花.没有蛋糕…… 姜小白就为大家备好了一份大礼,将自己近几年整理收藏的全网 ...

  9. Codeforces_711_B

    http://codeforces.com/problemset/problem/711/B 比较简单,过程有点繁琐,先找一行包含那个0的行,得到和,以此填出0位置的值,然后判断这个矩阵是否符合条件. ...

  10. 关于将笔记本电脑作为wifi热点的详细步骤

    常规做法直接度娘.如果出现无法打开wifi功能,可找对应解决方法. 1.先检查网卡是否支持承载网络,检查方法为在cmd中使用管理员权限运行,输入netsh wlan show drivers.查看支持 ...