參考文檔:Java SPI(Service Provider Interface)簡(jiǎn)介(http://blog.csdn.net/top_code/article/details/51934459)
一庞萍、SPI 是什么?
SPI 全稱為 (Service Provider Interface) ,是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制。
二、SPI如何使用?
一個(gè)服務(wù)(Service)通常指的是已知的接口或者抽象類薯鼠,服務(wù)提供方就是對(duì)這個(gè)接口或者抽象類的實(shí)現(xiàn),然后按照SPI 標(biāo)準(zhǔn)存放到資源路徑META-INF/services目錄下,文件的命名為該服務(wù)接口的全限定名番官。如有一個(gè)服務(wù)接口:
package com.ricky.codelab.spi;
public interface DemoService {
public String sayHi(String msg);
}
其實(shí)現(xiàn)類為:
package com.ricky.codelab.spi.impl;
import com.ricky.codelab.spi.DemoService;
public class DemoServiceImpl implements DemoService {
@Override
public String sayHi(String msg) {
return "Hello, "+msg;
}
}
那此時(shí)需要在META-INF/services中創(chuàng)建一個(gè)名為com.ricky.codelab.spi.DemoService的文件,其中的內(nèi)容就為該實(shí)現(xiàn)類的全限定名:com.ricky.codelab.spi.impl.DemoServiceImpl钢属。
如果該Service有多個(gè)服務(wù)實(shí)現(xiàn)徘熔,則每一行寫(xiě)一個(gè)服務(wù)實(shí)現(xiàn)(#后面的內(nèi)容為注釋),并且該文件只能夠是以UTF-8編碼淆党。
tip 1
如果有多個(gè)實(shí)現(xiàn)近顷,你取出的時(shí)候?yàn)榧希J(rèn)是文件實(shí)現(xiàn)類的順序加載類宁否,如果要自定義order方法窒升,以便取出所有服務(wù)實(shí)現(xiàn)類后,可以按照設(shè)定進(jìn)行排序
三慕匠、ServiceLoader的用途
之前我們只是配置好了SPI饱须,但是SPI是如何真正在java程序中使用的,必須依靠ServiceLoader來(lái)動(dòng)態(tài)加載Service的實(shí)現(xiàn)類了台谊。
許多開(kāi)發(fā)框架都使用了Java的SPI機(jī)制蓉媳,如java.sql.Driver的SPI實(shí)現(xiàn)(mysql驅(qū)動(dòng)、oracle驅(qū)動(dòng)等)锅铅、common-logging的日志接口實(shí)現(xiàn)酪呻、dubbo的擴(kuò)展實(shí)現(xiàn)等等。這些本質(zhì)上盐须,都是框架幫你用ServiceLoader做好了加載SPI等流程
META-INF/services/配置
在src/main/resources 下創(chuàng)建META-INF/services/目錄玩荠,并新建com.ricky.codelab.spi.DemoService文件,內(nèi)容如下:
#English implementation
com.ricky.codelab.spi.impl.EnglishDemoServiceImpl
#Chinese implementation
com.ricky.codelab.spi.impl.ChineseDemoServiceImpl
加載Service實(shí)現(xiàn)類
import java.util.Iterator;
import java.util.ServiceLoader;
import com.ricky.codelab.spi.DemoService;
ServiceLoader<DemoService> serviceLoader = ServiceLoader.load(DemoService.class);
Iterator<DemoService> it = serviceLoader.iterator();
while (it!=null && it.hasNext()) {
DemoService demoService = it.next();
System.out.println("class:"+demoService.getClass().getName()+"***"+demoService.sayHi("World"));
}
運(yùn)行結(jié)果
···
class:com.ricky.codelab.spi.impl.DemoServiceImplHello, World
class:com.ricky.codelab.spi.impl.ChineseDemoServiceImpl你好, World
···
知識(shí)儲(chǔ)備:
1.Meta-Inf文件夾
所謂META-INF, 說(shuō)白了就是存放一些meta information相關(guān)的文件的這么一個(gè)文件夾, 一般來(lái)說(shuō)盡量不要自己手工放置文件到這個(gè)文件夾贼邓。怎么理解這句話呢阶冈?就是說(shuō)這個(gè)文件夾應(yīng)該被看作是JAVA工程的一個(gè)內(nèi)部META目錄,所以這個(gè)目錄下的文件應(yīng)該都是build工具來(lái)生成的塑径。我們自己的文件應(yīng)該直接放到根目錄下或者其他的子目錄中女坑。
根據(jù)官方的JAR file specification(http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html), 一個(gè)典型的META-INF目錄下可能包含如下幾種文件或者子目錄:
MANIFEST.MF
INDEX.LIST
x.SF
x.DSA
services/
如果你將Jar中的META-INF文件夾刪除,那么jar文件里邊就沒(méi)有MANIFEST.MF文件统舀。那么匆骗,java -jar就找不到main class.
沒(méi)有META-INF你仍然可以創(chuàng)建一個(gè)Jar文件劳景。但是,當(dāng)你想要執(zhí)行jar文件的時(shí)候碉就,這個(gè)jar是需要具備 META-INF/MANIFEST.MF的枢泰。