瞭解ServiceLoader,須要先了解 SPI(Service Provider Interface)java
SPI的簡單來講就是在程序設計時將一個功能服務的接口與實現分離,在程序運行時經過JVM機制自動找到服務接口的實現類並建立,以達到解耦的目的,提升程序的可拓展性; 好比JDBCgit
ServiceLoader就是 Java平臺提供的一個簡單的 Service Provder Framework。使用ServiceLoader有簡單的如下幾個步驟github
這裏以一個簡單虛擬支付場景爲例。 有一個業務模塊目前須要使用支付服務,因此咱們首先建立了一個PaymenService抽象接口表示 支付服務,接口類中有一個抽象方法**pay(String productName,double price)**表示支付某個商品 ###建立服務實現模塊緩存
package com.knight.serviceimpl;
import com.knight.PaymentService;
public class PaymentServiceImpl implements PaymentService {
@Override
public void pay(String productName, double price) {
System.out.println("支付模塊:購買產品 "+productName +",價格"+price);
}
}
複製代碼
在IDEA中的結構以下 app
業務模塊中直接經過ServiceLoader類及PaymentService接口獲取服務實例並實現業務邏輯(業務模塊通常是不包含服務的實現模塊的)ide
package com.knight.business;
import com.knight.PaymentService;
import java.util.Iterator;
import java.util.ServiceLoader;
public class BusinessModule {
public static void run(){
Iterator<PaymentService> serviceIterator = ServiceLoader.load(PaymentService.class).iterator();
if (serviceIterator.hasNext()){
PaymentService paymentService = serviceIterator.next();
paymentService.pay("Q幣充值",100.00);
}else {
System.out.println("未找到支付模塊");
}
}
}
複製代碼
以上的核心代碼是 經過 ServiceLoader的load方法,傳入PaymentService接口類,會返回一個ServiceLoader的實例對象,經過該對象的**iterator()**方法會返回一個 Iterator 的迭代器,能夠經過這個迭代器獲得全部PaymentService的實現對象。gradle
最後 咱們再建立一個app模塊運行業務代碼邏輯,app模塊包含service、service-impl、business、3個模塊。ui
以上全部代碼已上傳 gitthis
ServiceLoader內部細節 1.首先經過靜態方法load獲取對應服務接口的ServiceLoader實例; 2.ServiceLoader類繼承了Iterabale接口,內部實現了一個服務實例懶加載的迭代器;迭代器內部經過classLoader讀 取對應META-INF/service/文件夾下的服務配置文件獲取到全部的實現類類名稱,當經過iterator()方法獲取迭代器後,就能夠依次實例化service的實現並將實現對象加入到緩存中。 3.解析配置文件的過程就是按行讀取,每一行的文本都是一個服務實現類的全限定名,獲取到類名就能夠經過反射實例化對象了google
public final class ServiceLoader<S> implements Iterable<S> {
//Service配置文件的資源路徑
private static final String PREFIX = "META-INF/services/";
// The class or interface representing the service being loaded
private final Class<S> service;
// 負責service配置資源加載,實例化service
private final ClassLoader loader;
// 服務實例的緩存,已被迭代被建立過的會加到這個cache中
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
reload();
}
//重載load, 刪除緩存並從新實例化iterator
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
//懶加載的service迭代器
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
//讀取配置文件,最終轉換成一個配置文件內容元素迭代器
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
//獲取配置文件中的下一個元素
nextName = pending.next();
return true;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
return hasNextService();
}
public S next() {
return nextService();
}
}
//按行讀取文件,每一行都是服務實現類的接口名
private Iterator<String> parse(Class<?> service, URL u)throws ServiceConfigurationError {
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}
}
複製代碼
以上 當註冊服務實現時若是須要手動建立文件並寫入服務實現類名稱 不免有些繁瑣,咱們可使用谷歌提供的 AutoService 庫簡化這一過程
dependencies {
compileOnly 'com.google.auto.service:auto-service:1.0-rc2'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
}
複製代碼
package com.knight.serviceimpl;
import com.google.auto.service.AutoService;
import com.knight.PaymentService;
@AutoService(PaymentService.class)
public class PaymentServiceImpl implements PaymentService {
@Override
public void pay(String productName, double price) {
System.out.println("支付模塊:購買產品 "+productName +",價格"+price);
}
}
複製代碼
3.項目編譯觸發auto-service註解處理過程後自動生成了配置文件
該實例源碼已上傳 git倉庫