一、SPI機制

        這裡先說下SPI的一個概念,SPI英文為Service Provider Interface單從字面可以理解為Service提供者介面,正如從SPI的名字去理解SPI就是Service提供者介面;我對SPI的定義:提供給服務提供廠商與擴充套件框架功能的開發者使用的介面。

       在我們日常開發的時候都是對問題進行抽象成Api然後就提供各種Api的實現,這些Api的實現都是封裝與我們的Jar中或框架中的雖然當我們想要提供一種Api新實現時可以不修改原來程式碼只需實現該Api就可以提供Api的新實現,但我們還是生成新Jar或框架(雖然可以通過在程式碼裡掃描某個目錄已載入Api的新實現,但這不是Java的機制,只是hack方法),而通過Java SPI機制我們就可以在不修改Jar包或框架的時候為Api提供新實現。

     很多框架都使用了java的SPI機制,如java.sql.Driver的SPI實現(MySQL驅動、oracle驅動等)、common-logging的日誌介面實現、dubbo的擴充套件實現等等框架;

SPI機制的約定:

1)         在META-INF/services/目錄中建立以介面全限定名命名的檔案該檔案內容為Api具體實現類的全限定名

2)         使用ServiceLoader類動態載入META-INF中的實現類

3)         如SPI的實現類為Jar則需要放在主程式classPath中

4)         Api具體實現類必須有一個不帶引數的構造方法

                                SPI機制結構圖

二、SPI機制示例

                        例項結構圖

IOperation介面:

package org.nercita.ltxx.spiTest;
/**
 * this is a Interface for two data 
 * @author zhangwenchao
 *
 */
public interface IOperation {
	 public int operation(int numberA, int numberB);
}

SPI介面的實現類:PlusOperationImpl

package org.nercita.ltxx.spiTest;

public class PlusOperationImpl implements IOperation {
    public int operation(int numberA, int numberB) {

        return numberA + numberB;

    }

}

SPI介面的實現類:DivisionOperationImpl

package org.nercita.ltxx.spiTest;

public class DivisionOperationImpl implements IOperation{
    public int operation(int numberA, int numberB) {

        return numberA / numberB;

    }
}

META-INF/Services目錄中的檔案:

檔名:org.nercita.ltxx.spiTest.IOperation

內容:

       org.nercita.ltxx.spiTest.DivisionOperationImpl
       org.nercita.ltxx.spiTest.PlusOperationImpl

測試工程引入上述jar包,測試Main類如下:

package org.nercita.itxx.spiClient;

import java.util.Iterator;
import java.util.ServiceLoader;

import org.nercita.ltxx.spiTest.DivisionOperationImpl;
import org.nercita.ltxx.spiTest.IOperation;
import org.nercita.ltxx.spiTest.PlusOperationImpl;

public class Main {
	
	public static void main(String[] args) {
        IOperation plus = new PlusOperationImpl();

        IOperation division = new DivisionOperationImpl();

        System.out.println(plus.operation(6, 3));

        System.out.println(division.operation(6, 3));

        ServiceLoader<IOperation> operations = ServiceLoader.load(IOperation.class);

        Iterator<IOperation> operationIterator = operations.iterator();

        System.out.println("classPath:"+System.getProperty("java.class.path"));

        while (operationIterator.hasNext()) {

            IOperation operation = operationIterator.next();

            System.out.println(operation.operation(6, 3));

        }
	}

}

執行結果:

9
2
classPath:D:\Workspaces\projects\spiClient\bin;D:\Workspaces\projects\spiClient\lib\spiTest-0.0.1-SNAPSHOT.jar
2
9