SPI简单解析
什么是SPI
一种服务加载方式,全名为Service Provider Interface,Service提供者接口
如果我们要抽象里面的模块,在面对对象编程当中,我们模块之间,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。
一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦
通俗例子解释:
例子:
JDK中有支持音乐播放,假设只支持mp3的播放,有些厂商想在这个基础之上支持mp4播放,有的想支持mp5播放,
而这些厂商都是第三方厂商,如果没有提供SPI这种实现标准,那就只有修改JAVA的源代码了。
有了SPI标准,JDK的爸爸SUN公司只需要提供一个播放接口,在实现播放的功能上通过ServiceLoad的方式加载服务,
那么第三方只需要实现这个播放接口,再按SPI标准的约定进行打包,再放到classpath下面就OK了
SPI的机制约定:
1) 在jar包的META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为Api具体实现类的全限定名 2) 服务调用方用java.util.ServiceLoader,用服务接口为参数,去动态加载具体的实现类到JVM中 3) 如SPI的实现类为Jar则需要放在主程序classPath中 4) 定义服务的通用接口,针对通用的服务接口,提供具体的实现类
//通用接口, public interface SendMessage { void send(String msg); }
package com.quan.spi.emailimp; import com.quan.spi.api.SendMessage; //通用接口的实现类1 public class SendMessageEmail implements SendMessage { @Override public void send(String msg) { System.out.println("send " + msg +" use email"); } }
package com.quan.spi.phoneimp; import com.quan.spi.api.SendMessage; //通用接口的实现类2 public class SendMessagePhone implements SendMessage { @Override public void send(String msg) { System.out.println("send " +msg +" use phone"); } }
//通过工厂模式去获得sendmessage public class SendMessageFactory { public SendMessageFactory() { } public static SendMessage getsend(){ SendMessage sendMessage = null; ServiceLoader<SendMessage> sendMessages = ServiceLoader.load(SendMessage.class); Iterator iterator = sendMessages.iterator(); while (iterator.hasNext()){ sendMessage = (SendMessage) iterator.next(); sendMessage.send("quan"); } return sendMessage; } }
META-INF/services:
名字为:com.quan.spi.api.SendMessage
com.quan.spi.emailimp.SendMessageEmail
com.quan.spi.phoneimp.SendMessagePhone
测试:
package com.quan.spi; import com.quan.spi.api.SendMessage; import org.junit.Test; import java.util.ServiceLoader; public class TestMessage { @Test public void Test(){ SendMessageFactory.getsend(); } @Test public void Test2(){ ServiceLoader<SendMessage> sendMessages = ServiceLoader.load(SendMessage.class); for (SendMessage s : sendMessages) { s.send("quanquan"); } } }