ExtensionLoader
這是構(gòu)成dubbo spi內(nèi)核的主要類,因此是閱讀dubbo源碼必須要先了解的類。
getExtensionLoader
ExtensionLoader的構(gòu)造方法是私有的,唯一得到實(shí)例的方法就是這個(gè)靜態(tài)方法。它確保了type是接口,并且通過(guò)withExtensionAnnotation方法確保接口上有SPI注解,然后構(gòu)造實(shí)例對(duì)象,并放入EXTENSION_LOADERS靜態(tài)域緩存。
getExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
//先從類實(shí)例緩存中加載
Map<String, Class<?>> classes = cachedClasses.get();
//經(jīng)典的單例寫(xiě)法
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//加載擴(kuò)展類并放入緩存
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
這是ExtensionLoader中一個(gè)無(wú)參的私有方法,用來(lái)加載spi,可以看到在類中的很多方法都會(huì)先調(diào)用該方法。為什么不在構(gòu)造方法中直接調(diào)用該方法呢?我覺(jué)得可能的原因是在真正使用時(shí)加載指定接口的擴(kuò)展,以節(jié)約資源。
Holder
注意上面的cachedClasses,他的類型是dubbo的Holder類。
public class Holder<T> {
//volatile保證值多線程可見(jiàn)
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
對(duì)于這個(gè)類的用途,不是很理解,并且和AtomicReference感覺(jué)有點(diǎn)相似。
偶然有一天看到一個(gè)場(chǎng)景,可能是其用途所在,僅個(gè)人理解。代碼如下:
public void test(boolean flag) throws Exception {
Thread.sleep(5000);
if (flag) {
System.out.println("now is true");
}
}
flag作為方法的入?yún)?,初始值肯定是?zhǔn)確的,但是在線程暫停的5秒內(nèi),很有可能外部實(shí)際的值已經(jīng)改變了,但是方法內(nèi)部判斷的依舊是舊值,此時(shí)就出現(xiàn)了錯(cuò)誤的結(jié)果。如果用Holder或者AtomicBoolean包起來(lái),那就可以得到當(dāng)前的準(zhǔn)確值。
- 注意以上對(duì)
Holder的理解是錯(cuò)誤的,查看Holder的實(shí)例是如何使用的就可發(fā)現(xiàn),其目的是在加鎖時(shí)有一個(gè)局部鎖!!
loadExtensionClasses
如果在緩存中沒(méi)有拿到擴(kuò)展類,那么就會(huì)調(diào)用該方法,加載所有的擴(kuò)展類。dubbo指定了三個(gè)資源目錄,分別為:META-INF/services/;META-INF/dubbo/;META-INF/dubbo/internal/。
private Map<String, Class<?>> loadExtensionClasses() {
//得到spi注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//得到默認(rèn)的擴(kuò)展對(duì)象名字
String value = defaultAnnotation.value();
if ((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));
}
//緩存默認(rèn)擴(kuò)展類名字
if (names.length == 1) cachedDefaultName = names[0];
}
}
//擴(kuò)展類名字和類對(duì)象的map
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//加載指定的三個(gè)目錄的文件名是接口全類名的文件
//這里replace應(yīng)該是dubbo加入apache項(xiàng)目后做的適配
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
loadDirectory
加載指定目錄的擴(kuò)展類。
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
//目錄+接口全類名
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls;
//得到ExtensionLoader的類加載器
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
//不是bootstrap加載器,直接使用類加載器加載資源
urls = classLoader.getResources(fileName);
} else {
//是bootstrap加載器,調(diào)用系統(tǒng)加載器
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//加載找到的所有的資源文件
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
loadResource
加載具體的某個(gè)資源文件。
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
//按行讀取文件內(nèi)容
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;
//根據(jù)=得到名字和擴(kuò)展類的全類名
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//處理該加載類
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
//緩存加載某個(gè)類出現(xiàn)的異常
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
這里將加載某個(gè)擴(kuò)展類出現(xiàn)的異常緩存了起來(lái),不直接拋出保證了可以繼續(xù)加載其他擴(kuò)展類,然后在使用該擴(kuò)展時(shí)從緩存中拿到該異常報(bào)錯(cuò)。
loadClass
加載資源文件內(nèi)的指定擴(kuò)展類。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判斷是否是接口的實(shí)現(xiàn)類
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注解,該注解表示該擴(kuò)展類是適配類
//然后放入cachedAdaptiveClass緩存,如果有多個(gè)報(bào)異常
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());
}
//判斷是否是包裝擴(kuò)展類,是就放入cachedWrapperClasses緩存
//判斷是否有該接口類型入?yún)⒌臉?gòu)造方法,可以查看isWrapperClass方法
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//確保有無(wú)參構(gòu)造方法
clazz.getConstructor();
//如果沒(méi)有用=指定名字就先通過(guò)Extension注解拿(不過(guò)貌似已經(jīng)不推薦了)
//再通過(guò)類名拿(類名后綴是接口類名的話會(huì)去除)
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
//名字支持,分隔
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//Activate注解表明其是激活擴(kuò)展,以后會(huì)講到這個(gè)注解的作用
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
//緩存激活擴(kuò)展類
cachedActivates.put(names[0], activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(names[0], oldActivate);
}
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
//放入緩存
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
//名字重復(fù)異常
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
本文總結(jié)
SPI
接口上必須要有該注解,該注解的值指定了默認(rèn)的擴(kuò)展實(shí)現(xiàn)的名字。
dubbo加載目錄
- META-INF/services/
- META-INF/dubbo/
- META-INF/dubbo/internal/
dubbo類名來(lái)源
- 資源文件中通過(guò)
=指定,可以通過(guò),分隔(,兩邊支持空格) - 通過(guò)
Extension注解指定(Deprecated) - 通過(guò)類名拿到(截去接口類名)
之前我們看到exceptions緩存了加載某個(gè)擴(kuò)展類出現(xiàn)的異常,緩存的鍵是文件的一行(去掉了注釋),后續(xù)是通過(guò)擴(kuò)展類名去拿緩存的,所以第二種方式顯然不能匹配得到。
擴(kuò)展類分類
- 默認(rèn)擴(kuò)展,spi注解指定
- 適配擴(kuò)展,Adaptive注解指定,只能有一個(gè)
- 包裝擴(kuò)展,有接口類型入?yún)⒌臉?gòu)造方法
- 激活擴(kuò)展,Activate指定,可以通過(guò)該注解的幾個(gè)值指定是否生效,filter就通過(guò)該注解
- 一般擴(kuò)展,cachedClasses緩存了名字和類的關(guān)系,cachedNames緩存了類和首個(gè)名字的關(guān)系