知識(shí)點(diǎn):SPI(Service Provider Interfaces)到底是啥玩意翘地,簡(jiǎn)單一句話概括就是用來(lái)被第三方實(shí)現(xiàn)的 API蛛倦。有點(diǎn)抽象年柠,我們先仿照Apollo客戶端寫(xiě)個(gè)簡(jiǎn)單例子吧
場(chǎng)景舉例
假設(shè)有一個(gè)短信平臺(tái)需要對(duì)接多家SMS渠道用來(lái)發(fā)送短信扰楼。當(dāng)然這個(gè)場(chǎng)景不一定要用SPI機(jī)制來(lái)實(shí)現(xiàn),畢竟條條大路通羅馬划鸽。本案例只是用來(lái)介紹SPI的使用方式方法输莺。
工程目錄
fg-spi-sms-interface 是接口部分,提供了一個(gè)默認(rèn)實(shí)現(xiàn)裸诽,fg-spi-sms-provider是自定義的一個(gè)實(shí)現(xiàn)
fg-spi-sms-interface
接口部分的代碼結(jié)構(gòu)如下嫂用,本模塊假設(shè)為通用實(shí)現(xiàn)
我們首先提供一個(gè)短信發(fā)送的接口或者抽象類,下面我們新建一個(gè)接口如下:
package com.example.fg.sms;
/**
* @author cattle - 稻草鳥(niǎo)人
* @date 2020/4/23 下午12:49
*/
public interface MessageServiceProvider {
/**
* 發(fā)送短息
* @param message 短信內(nèi)容
*/
void sendMessage(String message);
}
其次我們對(duì)這個(gè)接口做一個(gè)默認(rèn)實(shí)現(xiàn)如下:
package com.example.fg.sms;
/**
* @author cattle - 稻草鳥(niǎo)人
* @date 2020/4/23 下午12:57
*/
public class DefaultMessageServiceProvider implements MessageServiceProvider {
@Override
public void sendMessage(String message) {
System.out.println("default:::" + message);
}
}
再次丈冬,我們?cè)趓esources/META-INF/services目錄下新建一個(gè)文件嘱函,名為com.example.fg.sms.MessageServiceProvider
此名字和接口同名,內(nèi)容為com.example.fg.sms.DefaultMessageServiceProvider
是我們的默認(rèn)實(shí)現(xiàn)
最后,我們就可以通過(guò)ServiceLoader.load
埂蕊。例如往弓,通過(guò)如下代碼實(shí)現(xiàn)執(zhí)行我們默認(rèn)方法實(shí)現(xiàn)的方法
package com.example.fg.sms;
import java.util.ServiceLoader;
/**
* @author cattle - 稻草鳥(niǎo)人
* @date 2020/4/23 下午12:52
*/
public class MessageServiceFactory {
private ServiceLoader<MessageServiceProvider> serviceProviders = ServiceLoader.load(MessageServiceProvider.class);
public void sendMessage(String message) {
for (MessageServiceProvider serviceProvider : serviceProviders) {
serviceProvider.sendMessage(message);
}
}
}
接口測(cè)試
package com.example.fg.sms;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MessageServiceFactoryTest {
@Test
void sendMessage() {
MessageServiceFactory factory = new MessageServiceFactory();
factory.sendMessage("hello spi");
}
}
輸出:
default:::hello spi
fg-spi-sms-provider
本模塊是另外一個(gè)實(shí)現(xiàn),用來(lái)模擬某供應(yīng)商針對(duì)接口實(shí)現(xiàn)自己的短信發(fā)送方法蓄氧。
接口實(shí)現(xiàn)
package com.example.fg.sms;
/**
* @author cattle - 稻草鳥(niǎo)人
* @date 2020/4/23 下午1:06
*/
public class MyMessageServiceProvider implements MessageServiceProvider {
@Override
public void sendMessage(String message) {
System.out.println("MyMessage:::" + message);
}
}
本模塊我們依然要在resources/META-INF/services目錄下新建一個(gè)名為com.example.fg.sms.MessageServiceProvider
內(nèi)容就是我們自己的實(shí)現(xiàn)函似,即com.example.fg.sms.MyMessageServiceProvider
接口測(cè)試
package com.example.fg.sms;
import org.junit.jupiter.api.Test;
class MessageServiceFactoryTest {
@Test
void sendMessage() {
MessageServiceFactory factory = new MessageServiceFactory();
factory.sendMessage("hello spi");
}
}
輸出:
MyMessage:::hello spi
default:::hello spi
其他SPI場(chǎng)景
目前開(kāi)源的很多框架里面,如果大家仔細(xì)觀察的話匀们,SPI用到了非常多的地方缴淋,比如dubbo准给,motan泄朴,日志處理框架重抖,還有JDBC Driver等等。
總結(jié)
經(jīng)過(guò)上面的案例其實(shí)已經(jīng)很清楚了祖灰,寫(xiě)一個(gè)接口钟沛,然后自己去實(shí)現(xiàn),另外在resources/META-INF/services目錄下新建一個(gè)文件夾局扶,內(nèi)容是具體的實(shí)現(xiàn)恨统。然后通過(guò)ServiceLoader.load
之后調(diào)用具體的實(shí)現(xiàn)。
思考
多個(gè)實(shí)現(xiàn)的情況下三妈,如何只執(zhí)行其中一種實(shí)現(xiàn)呢畜埋?比如
MyMessageServiceProvider
和DefaultMessageServiceProvider
兩個(gè)實(shí)現(xiàn)只執(zhí)行其中一個(gè)sendMessage
方法apollo-client中對(duì)于SPI的具體應(yīng)用,和我們當(dāng)前的例子是否有差別呢畴蒲?