Dubbo原始碼解析之SPI
準備
dubbo版本:2.5.4
Dubbo SPI原始碼解析過程
Dubbo
在服務釋出過程中預設會載入自適應的協議擴充套件,在類 ServiceConfig
中存在以下初始化程式碼,下面以此進行SPI過程分析。
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
相關注解
-
@SPI
:表示當前這個介面是一個擴充套件點,可以實現自己的擴充套件實現,例如Protocol
介面預設擴充套件點實現是DubboProtocol
。 -
@Adaptive
:表示一個自適應擴充套件點,在方法級別上,會動態生成一個介面卡類。
getExtensionLoader
該方法需要一個 Class
型別的引數,該參數列示希望載入的擴充套件點型別,該引數必須是介面,且該介面必須被 @SPI
註解註釋,否則拒絕處理。檢查通過之後首先會檢查 ExtensionLoader
快取中是否已經存在該擴充套件對應的 ExtensionLoader
,如果有則直接返回,否則建立一個新的 ExtensionLoader
負責載入該擴充套件實現,同時將其快取起來。可以看到對於每一個擴充套件,dubbo
中只會有一個對應的 ExtensionLoader
例項
// ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if(!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
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;
}
getAdaptiveExtension
// ExtensionLoader
public T getAdaptiveExtension() {
// 從快取中獲取自適應的擴充套件點例項物件
Object instance = cachedAdaptiveInstance.get();
// 第一次載入,快取為空,則通過雙重檢驗鎖建立一個介面卡擴充套件點
if (instance == null) {
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 建立自適應擴充套件點>>
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
createAdaptiveExtension
// ExtensionLoader
private T createAdaptiveExtension() {
try {
// 1.首先通過getAdaptiveExtensionClass獲取介面卡擴充套件點Class並建立例項物件
// 2.通過injectExtension實現擴充套件點注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass
// ExtensionLoader
private Class<?> getAdaptiveExtensionClass() {
// 載入所有路徑下的擴充套件點>>
getExtensionClasses();
// 判斷當前擴充套件點(Protocol)是否存在自定義的介面卡,有則直接返回,沒有則動態建立
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 動態生成擴充套件點介面卡類>>
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 載入擴充套件點實現類>>
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
loadExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if(names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 載入META-INF/dubbo、META-INF/dubbo/internal、META-INF/services 三個目錄下的擴充套件點實現類
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
loadFile:解析指定路徑下檔案獲取對應擴充套件點,通過反射機制例項化
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
Class<?> clazz = Class.forName(line, true, classLoader);
// 判斷當前實現類是否是載入擴充套件點的實現類
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
/**
* 判斷是否有自定義介面卡類
* Adaptive註解加在類上,說明當前類就是自適應擴充套件點
* 加在方法上,則需要動態建立一個自適應擴充套件點(Protocol則對應Protocol$Adaptive)
*/
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
try {
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
回到 getAdaptiveExtensionClass
中的 createAdaptiveExtensionClass
方法
// ExtensionLoader
private Class<?> createAdaptiveExtensionClass() {
// 生成擴充套件點介面卡類位元組碼
String code = createAdaptiveExtensionClassCode();
// 獲取類載入器
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 動態編譯位元組碼
return compiler.compile(code, classLoader);
}
回到 createAdaptiveExtension
中的 injectExtension
擴充套件點注入方法
private T injectExtension(T instance) {
try {
if (this.objectFactory != null) {
Method[] var2 = instance.getClass().getMethods();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Method method = var2[var4];
// 判斷是否以set開頭,通過setter進行動態注入
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
// 獲取setter方法的引數型別
Class pt = method.getParameterTypes()[0];
try {
// 獲取屬性名稱
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 根絕型別及名稱獲取對應的擴充套件點
Object object = this.objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception var9) {
logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
}
}
}
}
} catch (Exception var10) {
logger.error(var10.getMessage(), var10);
}
return instance;
}
至此,整個擴充套件點載入過程完成。
相關文章
- Dubbo原始碼解析之SPI機制原始碼
- dubbo原始碼解析-spi(五)原始碼
- Dubbo之SPI原始碼分析原始碼
- Dubbo原始碼學習之-SPI介紹原始碼
- Dubbo原始碼解析之SPI(一):擴充套件類的載入過程原始碼套件
- Dubbo 原始碼分析 - SPI 機制原始碼
- JDK原始碼解析之Java SPI機制JDK原始碼Java
- Dubbo(一)-SPI(2) 機制之 Dubbo 的 SPI
- dubbo原始碼解析之負載均衡原始碼負載
- dubbo原始碼解析之基礎篇原始碼
- dubbo SPI功能解析(一)
- Dubbo原始碼解析之服務叢集原始碼
- Dubbo原始碼解析之負載均衡策略原始碼負載
- dubbo原始碼解析之ExtensionLoader類(二)原始碼
- 聊聊Dubbo(五):核心原始碼-SPI擴充套件原始碼套件
- Dubbo原始碼解析之服務呼叫過程原始碼
- Dubbo原始碼解析之服務引入過程原始碼
- Dubbo原理和原始碼解析之服務引用原始碼
- Dubbo SPI 原始碼學習 & admin安裝(二)原始碼
- Dubbo原始碼解析之服務匯出過程原始碼
- Dubbo原始碼解析之服務端接收訊息原始碼服務端
- Java SPI 與 Dubbo SPIJava
- Dubbo服務暴露原始碼解析②原始碼
- Dubbo原始碼解析之服務釋出與註冊原始碼
- Dubbo系列之 (一)SPI擴充套件套件
- Dubbo原始碼分析(三)Dubbo中的SPI和自適應擴充套件機制原始碼套件
- 聊聊Dubbo – Dubbo可擴充套件機制原始碼解析套件原始碼
- 溫習 SPI 機制 (Java SPI 、Spring SPI、Dubbo SPI)JavaSpring
- Dubbo(一)-SPI 機制之javaSPI基礎Java
- Dubbo原始碼之服務引用原始碼
- Dubbo服務呼叫過程原始碼解析④原始碼
- Dubbo原始碼學習之-通過原始碼看看dubbo對netty的使用原始碼Netty
- Dubbo原始碼解析之客戶端初始化及服務呼叫原始碼客戶端
- Dubbo原始碼之動態編譯原始碼編譯
- Dubbo之限流TpsLimitFilter原始碼分析MITFilter原始碼
- dubbo原始碼分析之叢集Cluster原始碼
- Dubbo原始碼分析之服務引用原始碼
- Dubbo原始碼分析之服務暴露原始碼