SPI是Service Provider Interface的简称,是JDK默认提供的一种将接口和实现类进行分离的机制。这种机制能将接口和实现进行解耦,大大提升系统的可扩展性。
SPI机制约定:当一个Jar包需要提供一个接口的实现类时,这个Jar包需要在META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该Jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
比如下面的列子,
jcl-over-slf4j
这个Jar包提供了conmon-logging中
LogFactory
这个接口的实现。
文件中的内容如下:
JDK为了方便查找 服务的实现,还提供了一个工具类:java.util.ServiceLoader。
上面代码中使用
ServiceLoader
遍历使用SPI机制提供的所有
LogFactory
实现。
SPI机制的主要应用有框架扩展和组件的替换等,比如
下面就一步步从定义接口到提供SPI实现类来演示下SPI机制具体的使用方式。
step1:先定义一个接口
<pre class="java" style="margin: 10px 0px; padding: 0px; white-space: pre !important; word-wrap: break-word; position: relative !important;">
Copy
`public interface SaySomething {
String say(String name);
}`</pre>
step2:编写实现类
<pre class="java" style="margin: 10px 0px; padding: 0px; white-space: pre !important; word-wrap: break-word; position: relative !important;">
Copy
public class ASaySomething implements SaySomething { @Override public String say(String name) { return "Hi,"+name+", l am A..."; } }
</pre>
step3:在resource下添加META-INFO/services目录
添加完这个目录后,添加一个以
SaySomething
接口的全限定名为名字的文件,这个文件的内容是你要设置的具体实现类。这边我们就设置实现类为上面的
ASaySomething
。
step4:使用SPI机制
<pre class="java" style="margin: 10px 0px; padding: 0px; white-space: pre !important; word-wrap: break-word; position: relative !important;">
Copy
public static void main(String[] args) { ServiceLoader<SaySomething> loader = ServiceLoader.load(SaySomething.class); loader.forEach(item ->{item.say("csx");}); }
</pre>
在开发中我们还经常会提到API这个名词,下面也总结下两者的区别:
API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。
SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。
优点
缺点
ServiceLoader
并不是线程安全的。免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。