1. 程式人生 > 實用技巧 >SPI簡單解析

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");
        }
    }



}