Java SPI全稱Service Provider Interface撬呢,是Java提供的一套用來被第三方實(shí)現(xiàn)或者擴(kuò)展的API玷禽,它可以用來啟用框架擴(kuò)展和替換組件鞍历。實(shí)際上是“基于接口的編程+策略模式+配置文件”組合實(shí)現(xiàn)的動態(tài)加載機(jī)制.
ServiceLoader的使用
操作步驟:
1、定義一個(gè)接口文件
2背蟆、寫出多個(gè)該接口文件的實(shí)現(xiàn)
3鉴分、在 src/main/resources/ 下建立 /META-INF/services 目錄, 新增一個(gè)以接口命名的文件 , 內(nèi)容是要接口的實(shí)現(xiàn)類全路徑
4带膀、使用ServiceLoader類 來獲取到這些實(shí)現(xiàn)的接口
先創(chuàng)建一個(gè)interface的module志珍。
然后在其他的模塊實(shí)現(xiàn)這個(gè)接口,并在實(shí)現(xiàn)這個(gè)接口的module中創(chuàng)建一個(gè)resources文件夾垛叨,文件夾里建一個(gè)文件碴裙,文件名為(對應(yīng)接口的全路徑 + 接口名),文件里面寫的內(nèi)容則為其(實(shí)現(xiàn)類的全路徑+實(shí)現(xiàn)類名)点额。在操作過程中舔株,遇到了一個(gè)坑,就是文件夾是兩級文件夾还棱,我在操作的時(shí)候圖簡單载慈,直接META-INF.services,結(jié)果導(dǎo)致通過ServiceLoader獲取實(shí)現(xiàn)類的時(shí)候,獲取為空珍手。這里應(yīng)該先創(chuàng)建一個(gè)文件夾办铡,命名為META-INF辞做,然后再在這個(gè)文件夾再創(chuàng)建一個(gè)文件夾,命名為services寡具,雖然在AS上顯示仍然為META-INF.services秤茅,但是這是一個(gè)二級文件夾 ,而我那種做法只算以及文件夾童叠。導(dǎo)致獲取不到文件里的內(nèi)容框喳。
然后創(chuàng)建一個(gè)help類,其具體的實(shí)現(xiàn)為:
public class ServiceHelper {
//針對一種接口多個(gè)實(shí)現(xiàn)類操作
public static <T> List<T> getServices(Class<T> interfaceClass){
ServiceLoader loader = ServiceLoader.load(interfaceClass);
Iterator iterator = loader.iterator();
List<T> list = new ArrayList<>();
while (iterator.hasNext()){
T t = (T) iterator.next();
if (t != null){
list.add(t);
}
}
return list;
}
//針對一個(gè)接口一個(gè)實(shí)現(xiàn)類操作
public static <T> T getService(Class<T> interfaceClass){
ServiceLoader loader = ServiceLoader.load(interfaceClass);
Iterator<T> iterator = loader.iterator();
if (iterator.hasNext()){
return (T) iterator.next();
}else {
return null;
}
}
}
然后在App模塊中調(diào)用厦坛,
AInterface aInterface = ServiceHelper.getService(AInterface.class);
Log.e("zzf",aInterface + "");
BInterface bInterface = ServiceHelper.getService(BInterface.class);
Log.e("zzf",aInterface.getname() +"------------" + bInterface.getname());
這種方法在平常組件化開發(fā)中非常便利五垮,但是每次都需要到/META-INF/services 目錄建立文件,不能動態(tài)添加杜秸。因此采用Google的@AutoService放仗,他可以幫我們在編譯的時(shí)候動態(tài)去生成這些東西。
ServiceLoader + @AutoService的使用
添加依賴:
implementation 'com.google.auto.service:auto-service:1.0'
annotationProcessor 'com.google.auto.service:auto-service:1.0'
接口module不需要變撬碟,我們只需要在實(shí)現(xiàn)類進(jìn)行改變一下即可:
@AutoService(CInterface.class)
public class C2Impl implements CInterface {
@Override
public String getName() {
return "C2Impl";
}
}
只需要在具體的實(shí)現(xiàn)類上面加上一個(gè)@AutoService注解诞挨,參數(shù)則為接口的class類。
然后在App模塊中調(diào)用呢蛤,
List<CInterface> list = ServiceHelper.getServices(CInterface.class);
for (CInterface cInterface:list){
Log.e("zzf",cInterface.getName());
}
ARouter
阿里的ARouter框架就是是借助了這種思想亭姥,它只需要我們的自己的接口繼承IProvider接口,
public interface BInterface extends IProvider {
String getname();
}
然后在B模塊實(shí)現(xiàn)接口
@Route(path = "/user/BInterface")
public BImpl implements BInterface{
@Override
public String getname() {
return "A2Impl";
}
}
然后在其他模塊通過ARouter注解獲取實(shí)例
@Autowired//(name = "/user/BInterface")
BImpl mBImpl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this);
...