SPI 全稱爲 Service Provider Interface,是一種服務發現機制。此機制在dubbo中大量使用。以至於dubbo框架的及其靈活。
dubbo SPI 源碼地址:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
1.java SPI源碼解析
1.1 SPI使用場景
dubbo官方中有java SPI的示例,但是初始並不是特別的理解。或者根本不理解這種設計的好處。直到看到mysql驅動包才恍然大悟。
java官方爸爸想連接數據庫,但是連接數據庫就要有數據庫驅動,但是流行的數據庫太多了,難道要一一實現麼?這樣太費事了。然後jdk就創建了個Driver
接口,此接口裏面定義瞭如下方法:
看不懂方法是幹啥的沒關係,反正不是普通開發人員來處理的。但是,如此就定義了標準,也就是說,如果想要java來連接數據庫,那就廠商或者想連接的開發人員自己去實現此接口就好。但是如果實現的多了java就不知道如何選擇了。比如,加了mysql的包,也加了oracle的包,或者自己也實現了這個接口,在多接口的時候,如何選擇就成了問題。這個時候SPI機制就有了作用了。
既然這樣,如果要連接mysql數據庫的話, 那麼mysql驅動包裏,一定要實現此接口。
如上發現,mysql包中確實是有實現Driver
接口。內容如下。
com.mysql.cj.jdbc.Driver
1.2 java SPI源碼解析
1.2.1 示例
假如有如下接口
public interface Robot {
void sayHello();
}
接下來定義兩個實現類,分別爲 OptimusPrime 和 Bumblebee。
public class OptimusPrime implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}
public class Bumblebee implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}
接下來 META-INF/services 文件夾下創建一個文件,名稱爲 Robot 的全限定名 org.apache.spi.Robot。文件內容爲實現類的全限定的類名,如下:
org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee
public class JavaSPITest {
@Test
public void sayHello() throws Exception {
ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
System.out.println("Java SPI");
for(Robot service : services) {
service.sayHello();
}
}
}
輸出:
Java SPI
Hello, I am Optimus Prime.
Hello, I am Bumblebee.
1.2.2解析
原本以爲內容加載是在load方法中處理,但是找了一圈也沒找到個啥。到最後卻發現在for循環中處理的。可能這樣看感覺很蒙,很簡單的for循環能有多少內容呀?
public static void main(String[] args) {
ServiceLoader<Robot> services = ServiceLoader.load(Robot.class);
Iterator var2 = services.iterator();
while(var2.hasNext()) {
Robot service = (Robot)var2.next();
service.sayHello();
}
}
代碼經過反編譯,卻變成了這樣。那一個迭代器,大家都知道的,能有啥內容呢?ServiceLoad
對迭代器中的內容進行了重寫。在執行services.iterator();
這段代碼的時候,是執行如下代碼。
這裏就涉及到了Iterable
和iterator
這兩個類的內容了,詳細看這篇博文,寫的挺詳細的https://www.cnblogs.com/litexy/p/9744241.html
public Iterator<S> iterator() {
return new Iterator<S>() {
// 用來做緩存的
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
// 主要關注這個就可以了
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
// 主要關注這個就可以了
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
那麼怎麼解析的,就只需要關注hasNext
方法和next
方法就好了。
1.2.3hasNext方法解析
public boolean hasNext() {
//訪問控制上下文 爲null
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
private boolean hasNextService() {
// 如果有值,直接返回了,這個是在下面進行設置。
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//PREFIX 是META-INF/services/ 這個值是固定死的
// service 是設置類的全名,包括路徑名
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;
}
1.2.4 next方法解析
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
//第一次走這裏
return lookupIterator.next();
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
// 獲取值,這個在hasNext的時候就設置好了。
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
}