什么是SPI
SPI是服务提供者接口的简称,是JDK默认提供的一种将接口和实现类进行分离的机制。这种机制能将接口和实现进行解耦,大大提升系统的可扩展性。
SPI机制约定:当一个Jar包需要提供一个接口的实现类时,这个Jar包需要在meta - inf/服务/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该Jar包meta - inf/服务/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
比如下面的列子, <代码> jcl-over-slf4j 代码>这个Jar包提供了conmon-logging中 <代码> LogFactory 代码>这个接口的实现。
图像
文件中的内容如下:
JDK为了方便查找服务的实现,还提供了一个工具类:java.util.ServiceLoader。
上面代码中使用 <代码>即便ServiceLoader> 代码遍历使用SPI机制提供的所有 <代码> LogFactory 代码>实现。
应用场景
SPI机制的主要应用有框架扩展和组件的替换等,比如
-
<李> JDBC接口实现类的运行时加载:我们连接具体的数据库是都需要添加相关的Jar包依赖,但是不需要我们再做任何其他配置,只要将Jar包放到类路径下就行了。这是一个最常见的SPI应用场景。李>
<李>日志门面加载具体的日志实现类:之前的博客中介绍到,jcl和slf4j等只是日志实现类,Log4j和LOgBack才是具体的日志实现.JCL和slf4j加载日志实现类时也使用了SPI机制,具体请看上面章节中举的列子。李>
<李>春天中大量使用了SPI:比如对servlet3.0规范对ServletContainerInitializer的实现,自动类型转换类型转换SPI(转炉SPI、格式化器SPI)等李>
自己实现
下面就一步步从定义接口到提供SPI实现类来演示下SPI机制具体的使用方式。
& lt;前类=癹ava"风格=氨Vそ?10 px 0 px;填充:0 px;空白:pre !重要;自动换行:break-word;位置:相对!重要;“在
复制
的公共接口SaySomething {
<>以前String 说(String 名称),>之前}’& lt;/pre>
& lt;前类=癹ava"风格=氨Vそ?10 px 0 px;填充:0 px;空白:pre !重要;自动换行:break-word;位置:相对!重要;“在
复制
<代码>公共类ASaySomething实现SaySomething {@Override公共字符串(字符串名称)说{返回“嗨,“+名称+“,我…“;;}}代码> & lt;/pre>
添加完这个目录后,添加一个以
<代码> SaySomething 代码>接口的全限定名为名字的文件,这个文件的内容是你要设置的具体实现类。这边我们就设置实现类为上面的
<代码> ASaySomething> 代码。
& lt;前类=癹ava"风格=氨Vそ?10 px 0 px;填充:0 px;空白:pre !重要;自动换行:break-word;位置:相对!重要;“在
复制
<代码>公共静态void main (String [] args) {ServiceLoader
API和SPI的比较
在开发中我们还经常会提到API这个名词、下面也总结下两者的区别:
-
<李>
API(应用程序编程接口)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。从使用人员上来说,API直接被应用开发人员使用。
李> <李>SPI(服务提供者接口)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。从使用人员上来说,SPI被框架扩展人员使用。
李>优缺点