一、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