框架設計的有彈性擴展性好會給使用者帶來很大的方便,如果業務有新的變化只要使用新的實現去替換之前的實現,且框架如何去加載實現我們也不用太關注,顯然這對我們開發者來説是非常友好的。java裏的SPI是這個思想,dubbo裏的SPI同樣是這個思想,關於java裏的spi因為會一次性將擴展點邏輯都執行而顯得不夠靈活,所以這裏不再展開(可以參考java spi),這裏重點説下dubbo裏的SPI.
使用方式
1,在META-INF/services目錄下根據接口的全路徑名創建一個文件,文件內容為key=value的形式
2,ExtensionLoader.getExtensionLoader(xxx.class).getExtension("key")這樣就可以獲取到我們自定義的實現了。
實現分析
下面我們對ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name)這行代碼展開下。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
//判斷給定type是否為接口
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//判斷給定接口上是否有SPI註解
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//先從map裏獲取ExtensionLoader,如果沒有進行創建
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
這全方法主要是獲取ExtensionLoader:
1,對給定的type進行判斷,一是判斷是否是接口類型;二是判斷接口上是否有SPI註解
2,根據給定的type先從Map獲取對應的ExtensionLoader,要是沒有進行創建。
接下來我們再看看getExtension(name)方法:
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
//先獲取包裝類Holder
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
//再從包裝類裏獲取實現類的實例,需要重點看下createExtension
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
getExtension(name)方法邏輯清晰明瞭:
1,先獲取包裝類Holder
2,從包裝類Holder裏獲取具體實現類的實例。
實現類的實例是如何創建的呢?再看下createExtension(name)
private T createExtension(String name) {
/**
* getExtensionClasses會加載META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/這三
* 個目錄下的所有類
*/
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
//通過反射的方式創建name對應的實例,並放到map裏,下次可以直接從map裏取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
/**
* 如果實現類對象裏有set方法,則框架還會根據依賴對象的實際情況進行自動注入的工作
* 説明,只有滿足下面兩個條件才會進行注入:
* 1,被依賴的對象也是在services,dubbo,internal這三個目錄下
* 2,set方法上沒有使用了DisableInject註解
*/
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
這方法的主要邏輯:
1,通過Class.forName加載META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/這三個目錄下的所有類
2,通過反射創建type對應的實例
3,如果符合注入條件,框架還會進行自動注入。
最後用一張圖總結下整個過程:
創建完對象後都會將對象放入緩存方便下次直接獲取。