前面我們了解過了Java的SPI擴展機制,對于Java擴展機制的原理以及優缺點也有了大概的了解,這里繼續深入一下Dubbo的擴展點加載機制。
Dubbo擴展點加載的功能
Dubbo的擴展點加載機制類似于Java的SPI,我們知道Java的SPI在使用的時候,只能通過遍歷來進行實現的查找和實例化,有可能會一次性把所有的實現都實例化,這樣會造成有些不使用的擴展實現也會被實例化,這就會造成一定的資源浪費。有關Dubbo的改進,參照文檔上的說明:
- JDK標準的SPI會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上也加載,會很浪費資源。
- 如果擴展點加載失敗,連擴展點的名稱都拿不到了。比如:JDK標準的ScriptEngine,通過getName();獲取腳本類型的名稱,但如果RubyScriptEngine因為所依賴的jruby.jar不存在,導致RubyScriptEngine類加載失敗,這個失敗原因被吃掉了,和ruby對應不起來,當用戶執行ruby腳本時,會報不支持ruby,而不是真正失敗的原因。
- 增加了對擴展點IoC和AOP的支持,一個擴展點可以直接setter注入其它擴展點。
關于第一點,通過和Java的SPI對比,就能明白;第二點還未做測試,不太清楚其中的緣由;第三點對于IOC和AOP的支持下面簡單介紹下。
擴展點自動裝配功能(IOC)
就是當加載一個擴展點時,會自動的注入這個擴展點所依賴的其他擴展點,如果描述不清楚的話,可以看下下面的例子:
接口A,實現類A1,A2
接口B,實現類B1,B2
其中實現類A1含有setB()方法,當通過擴展機制加載A的實現的時候,會自動的注入一個B的實現類,但是,此時不是注入B1,也不是注入B2,而是注入一個自適應的B的實現類:B$Adpative
,該實現類是動態生成的,能夠根據參數的不同,自動選擇B1或者B2來進行調用。
擴展點自適應
上面我們說,在自動裝配的時候,并不是注入一個真正的實現,而是注入一個自適應的擴展點實現,其實就是動態的生成的代碼,也就是手動拼裝的代碼,這段代碼里會根據SPI上配置的信息來加入對于具體實現的選擇功能。生成的代碼類似于下面的,代碼做了一下精簡,把包都去掉了:
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements Protocol {
public Invoker refer(Class arg0, URL arg1) throws Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public Exporter export(Invoker arg0) throws Invoker {
if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("Invoker argument getUrl() == null");URL url = arg0.getUrl();
//這里會根據url中的信息獲取具體的實現類名
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
//根據上面的實現類名,會在運行時,通過Dubbo的擴展機制加載具體實現類
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public void destroy() {
throw new UnsupportedOperationException("method public abstract void Protocol.destroy() of interface Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int Protocol.getDefaultPort() of interface Protocol is not adaptive method!");
}
}
使用這種方式的原因也很容易能想到,在我們加載擴展點實現的時候,并沒有調用實現的具體邏輯,那我們注入一個擴展點,也就不知道這個擴展點的實現具體是什么,所以要注入一個自適應的實現。等到運行時候,才根據自適應實現,來調用真正實現。
擴展點自動包裝功能(AOP)
先看下下面的示例,假如接口A還有另外一個實現者:AWrapper1:
class AWrapper1 implements A{
private A a;
AWrapper1(A a){
this.a = a;
}
}
AWrapper1相當于A的包裝類,類似于AOP的功能,AWrapper1增加了A的功能。當我們獲取接口A的實現類的時候,得到的就是包裝過的類。
Dubbo擴展點加載的實現
首先還是定義接口,然后是接口的具體實現類,配置文件類似于Java的SPI配置文件,Dubbo的配置文件放在META-INF/dubbo/
目錄下,配置文件名為接口的全限定名,配置文件內容是配置名=擴展實現類的全限定名
,加載實現類的功能是通過ExtensionLoader來實現,類似于Java中的ServiceLoader的作用。
另外,擴展點使用單一實例加載,需要確保線程安全性。
Dubbo擴展點加載的一些定義
@SPI
注解,被此注解標記的接口,就表示是一個可擴展的接口。-
@Adaptive
注解,有兩種注解方式:一種是注解在類上,一種是注解在方法上。- 注解在類上,而且是注解在實現類上,目前dubbo只有AdaptiveCompiler和AdaptiveExtensionFactory類上標注了此注解,這是些特殊的類,ExtensionLoader需要依賴他們工作,所以得使用此方式。
- 注解在方法上,注解在接口的方法上,除了上面兩個類之外,所有的都是注解在方法上。ExtensionLoader根據接口定義動態的生成適配器代碼,并實例化這個生成的動態類。被Adaptive注解的方法會生成具體的方法實現。沒有注解的方法生成的實現都是拋不支持的操作異常UnsupportedOperationException。被注解的方法在生成的動態類中,會根據url里的參數信息,來決定實際調用哪個擴展。
比如說這段代碼:
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
當上面代碼執行的時候,我們其實還不知道要真正使用的Protocol是什么,可能是具體的實現DubboProtocol,也可能是其他的具體實現的Protocol,那么這時候refprotocol到底是什么呢?refprotocol其實是在調用getAdaptiveExtension()方法時候,自動生成的一個類,代碼如下:
import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements Protocol { public Invoker refer(Class arg0, URL arg1) throws Class { if (arg1 == null) throw new IllegalArgumentException("url == null"); URL url = arg1; String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])"); Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } public Exporter export(Invoker arg0) throws Invoker { if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("Invoker argument getUrl() == null");URL url = arg0.getUrl(); String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])"); Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName); return extension.export(arg0); } public void destroy() { throw new UnsupportedOperationException("method public abstract void Protocol.destroy() of interface Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("method public abstract int Protocol.getDefaultPort() of interface Protocol is not adaptive method!"); } }
可以看到被@Adaptive注解的方法都生成了具體的實現,并且實現邏輯都相同。而沒有被注解的方法直接拋出不支持操作的異常。
當我們使用refprotocol調用方法的時候,其實是調用生成的類
Protocol$Adpative
中的方法,這里面的方法根據url中的參數配置來找到具體的實現類,找具體實現類的方式還是通過dubbo的擴展機制。比如url中可能會有protocol=dubbo,此時就可以根據這個dubbo來確定我們要找的類是DubboProtocol。可以查看下生成的代碼中getExtension(extName)
這里是根據具體的名字去查找實現類。 @Activate
注解,此注解需要注解在類上或者方法上,并注明被激活的條件,以及所有的被激活實現類中的排序信息。ExtensionLoader,是dubbo的SPI機制的查找服務實現的工具類,類似與Java的ServiceLoader,可做類比。dubbo約定擴展點配置文件放在classpath下的
/META-INF/dubbo,/META-INF/dubbo/internal,/META-INF/services
目錄下,配置文件名為接口的全限定名,配置文件內容為配置名=擴展實現類的全限定名
。
Dubbo擴展點加載的源碼解析
重點解析下ExtensionLoader這個類。Dubbo的擴展點使用單一實例去加載,緩存在ExtensionLoader中。每一個ExtensionLoader實例僅負責加載特定SPI擴展的實現,想要獲得某個擴展的實現,首先要獲得該擴展對應的ExtensionLoader實例。
以Protocol為例進行分析擴展點的加載:
//這樣使用,先獲取ExtensionLoader實例,然后加載自適應的Protocol擴展點
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//使用
protocol.refer(Class<T> type, URL url));
可以看到,使用擴展點加載的步驟大概有三步:
- 獲取ExtensionLoader實例。
- 獲取自適應實現。
- 使用獲取到的實現。
下面我們就以這三步作為分界,來深入源碼的解析。
獲取ExtensionLoader實例
第一步,getExtensionLoader(Protocol.class),根據要加載的接口Protocol,創建出一個ExtensionLoader實例,加載完的實例會被緩存起來,下次再加載Protocol的ExtensionLoader的時候,會使用已經緩存的這個,不會再新建一個實例:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//擴展點類型不能為空
if (type == null)
throw new IllegalArgumentException();
//擴展點類型只能是接口類型的
if(!type.isInterface()) {
throw new IllegalArgumentException();
}
//沒有添加@SPI注解,只有注解了@SPI的才會解析
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException();
}
//先從緩存中獲取指定類型的ExtensionLoader
//EXTENSION_LOADERS是一個ConcurrentHashMap,緩存了所有已經加載的ExtensionLoader的實例
//比如這里加載Protocol.class,就以Protocol.class作為key,以新創建的ExtensionLoader作為value
//每一個要加載的擴展點只會對應一個ExtensionLoader實例,也就是只會存在一個Protocol.class在緩存中
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//緩存中不存在
if (loader == null) {
//創建一個新的ExtensionLoader實例,放到緩存中去
//對于每一個擴展,dubbo中只有一個對應的ExtensionLoader實例
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
上面代碼返回一個ExtensionLoader實例,getExtensionLoader(Protocol.class)
這一步沒有進行任何的加載工作,只是獲得了一個ExtensionLoader的實例。
ExtensionLoader的構造方法
上面獲取的是一個ExtensionLoader實例,接著看下構造實例的時候到底做了什么,我們發現在ExtensionLoader中只有一個私有的構造方法:
private ExtensionLoader(Class<?> type) {
//接口類型
this.type = type;
//對于擴展類型是ExtensionFactory的,設置為null
//getAdaptiveExtension方法獲取一個運行時自適應的擴展類型
//每個Extension只能有一個@Adaptive類型的實現,如果么有,dubbo會自動生成一個類
//objectFactory是一個ExtensionFactory類型的屬性,主要用于加載需要注入的類型的實現
//objectFactory主要用在注入那一步,詳細說明見注入時候的說明
//這里記住非ExtensionFactory類型的返回的都是一個AdaptiveExtensionFactory
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
不難理解,ExtensionFactory是主要是用來加載被注入的類的實現,分為SpiExtensionFactory和SpringExtensionFactory兩個,分別用來加載SPI擴展實現和Spring中bean的實現。
獲取自適應實現
上面返回一個ExtensionLoader的實例之后,開始加載自適應實現,加載是在調用getAdaptiveExtension()方法中進行的:
getAdaptiveExtension()-->
createAdaptiveExtension()-->
getAdaptiveExtensionClass()-->
getExtensionClasses()-->
loadExtensionClasses()
先看下getAdaptiveExtension()方法,用來獲取一個擴展的自適應實現類,最后返回的自適應實現類是一個類名為Protocol$Adaptive
的類,并且這個類實現了Protocol接口:
public T getAdaptiveExtension() {
//先從實例緩存中查找實例對象
//private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//在當前的ExtensionLoader中保存著一個Holder實例,用來緩存自適應實現類的實例
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {//緩存中不存在
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
//獲取鎖之后再檢查一次緩存中是不是已經存在
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//緩存中沒有,就創建新的AdaptiveExtension實例
instance = createAdaptiveExtension();
//新實例加入緩存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {createAdaptiveInstanceError = t; }
}
}
}
}
return (T) instance;
}
創建自適應擴展
緩存中不存在自適應擴展的實例,表示還沒有創建過自適應擴展的實例,接下來就是創建自適應擴展實現,createAdaptiveExtension()方法,用來創建自適應擴展類的實例:
private T createAdaptiveExtension() {
try {
//先通過getAdaptiveExtensionClass獲取AdaptiveExtensionClass
//然后獲取其實例
//最后進行注入處理
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {}
}
獲取自適應擴展類
接著查看getAdaptiveExtensionClass()方法,用來獲取一個自適應擴展的Class,這個Class將會在下一步被實例化:
private Class<?> getAdaptiveExtensionClass() {
//加載當前Extension的所有實現(這里舉例是Protocol,只會加載Protocol的所有實現類),如果有@Adaptive類型的實現類,會賦值給cachedAdaptiveClass
//目前只有AdaptiveExtensionFactory和AdaptiveCompiler兩個實現類是被注解了@Adaptive
//除了ExtensionFactory和Compiler類型的擴展之外,其他類型的擴展都是下面動態創建的的實現
getExtensionClasses();
//加載完所有的實現之后,發現有cachedAdaptiveClass不為空
//也就是說當前獲取的自適應實現類是AdaptiveExtensionFactory或者是AdaptiveCompiler,就直接返回,這兩個類是特殊用處的,不用代碼生成,而是現成的代碼
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//沒有找到Adaptive類型的實現,動態創建一個
//比如Protocol的實現類,沒有任何一個實現是用@Adaptive來注解的,只有Protocol接口的方法是有注解的
//這時候就需要來動態的生成了,也就是生成Protocol$Adaptive
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
加載擴展類實現
先看下getExtensionClasses()這個方法,加載所有的擴展類的實現:
private Map<String, Class<?>> getExtensionClasses() {
//從緩存中獲取,cachedClasses也是一個Holder,Holder這里持有的是一個Map,key是擴展點實現名,value是擴展點實現類
//這里會存放當前擴展點類型的所有的擴展點的實現類
//這里以Protocol為例,就是會存放Protocol的所有實現類
//比如key為dubbo,value為com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
//cachedClasses擴展點實現名稱對應的實現類
Map<String, Class<?>> classes = cachedClasses.get();
//如果為null,說明沒有被加載過,就會進行加載,而且加載就只會進行這一次
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//如果沒有加載過Extension的實現,進行掃描加載,完成后緩存起來
//每個擴展點,其實現的加載只會這執行一次
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
看下loadExtensionClasses()方法,這個方法中加載擴展點的實現類:
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
//當前Extension的默認實現名字
//比如說Protocol接口,注解是@SPI("dubbo")
//這里dubbo就是默認的值
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();
}
//默認的名字保存起來
if(names.length == 1) cachedDefaultName = names[0];
}
}
//下面就開始從配置文件中加載擴展實現類
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//從META-INF/dubbo/internal目錄下加載
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
//從META-INF/dubbo/目錄下加載
loadFile(extensionClasses, DUBBO_DIRECTORY);
//從META-INF/services/下加載
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
從各個位置的配置文件中加載實現類,對于Protocol來說加載的文件是以com.alibaba.dubbo.rpc.Protocol
為名稱的文件,文件的內容是(有好幾個同名的配置文件,這里直接把內容全部寫在了一起):
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
memcached=memcom.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
看下loadFile()方法:
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
//配置文件的名稱
//這里type是擴展類,比如com.alibaba.dubbo.rpc.Protocol類
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);
//查看類型是否匹配
//type是Protocol接口
//clazz就是Protocol的各個實現類
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException();
}
//如果實現類是@Adaptive類型的,會賦值給cachedAdaptiveClass,這個用來存放被@Adaptive注解的實現類
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException();
}
} else {//不是@Adaptice類型的類,就是沒有注解@Adaptive的實現類
try {//判斷是否是wrapper類型
//如果得到的實現類的構造方法中的參數是擴展點類型的,就是一個Wrapper類
//比如ProtocolFilterWrapper,實現了Protocol類,
//而它的構造方法是這樣public ProtocolFilterWrapper(Protocol protocol)
//就說明這個類是一個包裝類
clazz.getConstructor(type);
//cachedWrapperClasses用來存放當前擴展點實現類中的包裝類
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//沒有上面提到的構造器,則說明不是wrapper類型
//獲取無參構造
clazz.getConstructor();
//沒有名字,就是配置文件中沒有xxx=xxxx.com.xxx這種
if (name == null || name.length() == 0) {
//去找@Extension注解中配置的值
name = findAnnotationName(clazz);
//如果還沒找到名字,從類名中獲取
if (name == null || name.length() == 0) {
//比如clazz是DubboProtocol,type是Protocol
//這里得到的name就是dubbo
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(");
}
}
}
//有可能配置了多個名字
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//是否是Active類型的類
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
//第一個名字作為鍵,放進cachedActivates這個map中緩存
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
//放入Extension實現類與名稱映射的緩存中去,每個class只對應第一個名稱有效
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
//放入到extensionClasses緩存中去,多個name可能對應一份extensionClasses
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException();
}
}
}
}
}
}
} catch (Throwable t) { }
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) { }
} // end of while urls
}
} catch (Throwable t) { }
}
到這里加載當前Extension的所有實現就已經完成了,繼續返回getAdaptiveExtensionClass中,在調用完getExtensionClasses()之后,會首先檢查是不是已經有@Adaptive注解的類被解析并加入到緩存中了,如果有就直接返回,這里的cachedAdaptiveClass中現在只能是AdaptiveExtensionFactory或者AdaptiveCompiler中的一個,如果沒有,說明是一個普通擴展點,就動態創建一個,比如會創建一個Protocol$Adaptive
。
創建自適應擴展類的代碼
看下createAdaptiveExtensionClass()這個方法,用來動態的創建自適應擴展類:
private Class<?> createAdaptiveExtensionClass() {
//組裝自適應擴展點類的代碼
String code = createAdaptiveExtensionClassCode();
//獲取到應用的類加載器
ClassLoader classLoader = findClassLoader();
//獲取編譯器
//dubbo默認使用javassist
//這里還是使用擴展點機制來找具體的Compiler的實現
//現在就知道cachedAdaptiveClass是啥意思了,如果沒有AdaptiveExtensionFactory和AdaptiveCompiler這兩個類,這里又要去走加載流程然后來生成擴展點類的代碼,不就死循環了么。
//這里解析Compiler的實現類的時候,會在getAdaptiveExtensionClass中直接返回
//可以查看下AdaptiveCompiler這個類,如果我們沒有指定,默認使用javassist
//這里Compiler是JavassistCompiler實例
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//將代碼轉換成Class
return compiler.compile(code, classLoader);
}
接著看下createAdaptiveExtensionClassCode()方法,用來組裝自適應擴展類的代碼(拼寫源碼,代碼比較長不在列出),這里列出生成的Protocol$Adaptive
:
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
}
其他具體的擴展點的生成也類似。在生成完代碼之后,是找到ClassLoader,然后獲取到Compiler的自適應實現,這里得到的就是AdaptiveCompiler,最后調用compiler.compile(code, classLoader);
來編譯上面生成的類并返回,先進入AdaptiveCompiler的compile方法:
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
//得到一個ExtensionLoader
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
//默認的Compiler名字
String name = DEFAULT_COMPILER; // copy reference
//有指定了Compiler名字,就使用指定的名字來找到Compiler實現類
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {//沒有指定Compiler名字,就查找默認的Compiler的實現類
compiler = loader.getDefaultExtension();
}
//調用具體的實現類來進行編譯
return compiler.compile(code, classLoader);
}
獲取指定名字的擴展
先看下根據具體的名字來獲取擴展的實現類loader.getExtension(name);
,loader是ExtensionLoader<Compiler>
類型的。這里就是比Java的SPI要方便的地方,Java的SPI只能通過遍歷所有的實現類來查找,而dubbo能夠指定一個名字查找。代碼如下:
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如果name指定為true,則獲取默認實現
if ("true".equals(name)) {
//默認實現查找在下面解析
return getDefaultExtension();
}
//先從緩存獲取Holder,cachedInstance是一個ConcurrentHashMap,鍵是擴展的name,值是一個持有name對應的實現類實例的Holder。
Holder<Object> holder = cachedInstances.get(name);
//如果當前name對應的Holder不存在,就創建一個,添加進map中
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
//從Holder中獲取保存的實例
Object instance = holder.get();
//不存在,就需要根據這個name找到實現類,實例化一個
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//緩存不存在,創建實例
instance = createExtension(name);
//加入緩存
holder.set(instance);
}
}
}
//存在,就直接返回
return (T) instance;
}
創建擴展實例,createExtension(name);
:
private T createExtension(String name) {
//getExtensionClasses加載當前Extension的所有實現
//上面已經解析過,返回的是一個Map,鍵是name,值是name對應的Class
//根據name查找對應的Class
Class<?> clazz = getExtensionClasses().get(name);
//如果這時候class還不存在,說明在所有的配置文件中都沒找到定義,拋異常
if (clazz == null) {
throw findException(name);
}
try {
//從已創建實例緩存中獲取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
//不存在的話就創建一個新實例,加入到緩存中去
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//屬性注入
injectExtension(instance);
//Wrapper的包裝
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) { }
}
有關屬性注入和Wrapper的包裝,下面再講。到這里Compiler就能獲得到一個指定name的具體實現類的實例了,然后就是調用實例的compile()方法對生成的代碼進行編譯。
獲取默認擴展實現
如果在AdaptiveCompiler中沒有找到指定的名字,就會找默認的擴展實現loader.getDefaultExtension();
:
public T getDefaultExtension() {
//首先還是先去加載所有的擴展實現
//加載的時候會設置默認的名字cachedDefaultName,這個名字是在@SPI中指定的,比如Compiler就指定了@SPI("javassist"),所以這里是javassist
getExtensionClasses();
if(null == cachedDefaultName || cachedDefaultName.length() == 0
|| "true".equals(cachedDefaultName)) {
return null;
}
//根據javassist這個名字去查找擴展實現
//具體的過程上面已經解析過了
return getExtension(cachedDefaultName);
}
關于javassist編譯Class的過程暫先不說明。我們接著流程看:
private T createAdaptiveExtension() {
try {
//先通過getAdaptiveExtensionClass獲取AdaptiveExtensionClass(在上面這一步已經解析了,獲得到了一個自適應實現類的Class)
//然后獲取其實例,newInstance進行實例
//最后進行注入處理injectExtension
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) { }
}
擴展點注入
接下來就是有關擴展點的注入的問題了,injectExtension,關于注入的解釋查看最上面擴展點自動裝配(IOC)的說明,injectExtension方法:
//這里的實例是Xxxx$Adaptive
private T injectExtension(T instance) {
try {
//關于objectFactory的來路,先看下面的解析
//這里的objectFactory是AdaptiveExtensionFactory
if (objectFactory != null) {
//遍歷擴展實現類實例的方法
for (Method method : instance.getClass().getMethods()) {
//只處理set方法
//set開頭,只有一個參數,public
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
//set方法參數類型
Class<?> pt = method.getParameterTypes()[0];
try {
//setter方法對應的屬性名
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
//根據類型和名稱信息從ExtensionFactory中獲取
//比如在某個擴展實現類中會有setProtocol(Protocol protocol)這樣的set方法
//這里pt就是Protocol,property就是protocol
//AdaptiveExtensionFactory就會根據這兩個參數去查找對應的擴展實現類
//這里就會返回Protocol$Adaptive
Object object = objectFactory.getExtension(pt, property);
if (object != null) {//說明set方法的參數是擴展點類型,進行注入
//為set方法注入一個自適應的實現類
method.invoke(instance, object);
}
} catch (Exception e) { }
}
}
}
} catch (Exception e) {}
return instance;
}
有關AdaptiveExtensionFactory中獲取Extension的過程,會首先在實例化的時候得到ExtensionFactory的具體實現類,然后遍歷每個ExtensionFactory的實現類,分別在每個ExtensionFactory的實現類中獲取Extension。
這里使用SpiExtensionFactory的獲取擴展的方法為例,getExtension,也是先判斷給定類是否是注解了@SPI的接口,然后根據類去獲取ExtensionLoader,在使用得到的ExtensionLoader去加載自適應擴展。
objectFactory的來歷
objectFactory的來路,在ExtensionLoader中有個私有構造器:
//當我們調用getExtensionLoader這個靜態方法的時候,會觸發ExtensionLoader類的實例化,會先初始化靜態變量和靜態塊,然后是構造代碼塊,最后是構造器的初始化
private ExtensionLoader(Class<?> type) {
this.type = type;
//這里會獲得一個AdaptiveExtensionFactory
//根據類型和名稱信息從ExtensionFactory中獲取
//獲取實現
//為什么要使用對象工廠來獲取setter方法中對應的實現?
//不能通過spi直接獲取自適應實現嗎?比如ExtensionLoader.getExtension(pt);
//因為setter方法中有可能是一個spi,也有可能是普通的bean
//所以此時不能寫死通過spi獲取,還需要有其他方式來獲取實現進行注入
// dubbo中有兩個實現,一個是spi的ExtensionFactory,一個是spring的ExtensionFactory
//如果還有其他的,我們可以自定義ExtensionFactory
//objectFactory是AdaptiveExtensionFactory實例
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
到此為止createAdaptiveExtension方法解析完成,接著返回上層getAdaptiveExtension()方法中,發現創建完自適應擴展實例之后,就會加入到cachedAdaptiveInstance緩存起來,然后就會返回給調用的地方一個Xxx$Adaptive
實例。
走到這里,下面的代碼就解析完了:
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
得到擴展之后的使用
我們得到了一個Protocol$Adaptive
實例,接著就是調用了,比如說我們要調用refprotocol.refer(Class<T> type, URL url))
方法,由于這里refprotocol是一個Protocol$Adaptive
實例,所以就先調用這個實例的refer方法,這里的實例的代碼在最上面:
//這里為了好看,代碼做了精簡,包名都去掉了
public Invoker refer(Class arg0, URL arg1) throws Class {
if (arg1 == null) throw new IllegalArgumentException();
URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException();
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
可以看到這里首先根據url中的參數獲取擴展名字,如果url中沒有就使用默認的擴展名,然后根據擴展名去獲取具體的實現。關于getExtension(String name)上面已經解析過一次,這里再次列出:
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如果name指定為true,則獲取默認實現
if ("true".equals(name)) {
//默認實現查找在下面解析
return getDefaultExtension();
}
//先從緩存獲取Holder,cachedInstance是一個ConcurrentHashMap,鍵是擴展的name,值是一個持有name對應的實現類實例的Holder。
Holder<Object> holder = cachedInstances.get(name);
//如果當前name對應的Holder不存在,就創建一個,添加進map中
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
//從Holder中獲取保存的實例
Object instance = holder.get();
//不存在,就需要根據這個name找到實現類,實例化一個
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//緩存不存在,創建實例
instance = createExtension(name);
//加入緩存
holder.set(instance);
}
}
}
//存在,就直接返回
return (T) instance;
}
創建擴展實例,createExtension(name);
:
private T createExtension(String name) {
//getExtensionClasses加載當前Extension的所有實現
//上面已經解析過,返回的是一個Map,鍵是name,值是name對應的Class
//根據name查找對應的Class
//比如name是dubbo,Class就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
Class<?> clazz = getExtensionClasses().get(name);
//如果這時候class還不存在,說明在所有的配置文件中都沒找到定義,拋異常
if (clazz == null) {
throw findException(name);
}
try {
//從已創建實例緩存中獲取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
//不存在的話就創建一個新實例,加入到緩存中去
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//這里實例就是具體實現的實例了比如是DubboProtocol的實例
//屬性注入,在上面已經解析過了,根據實例中的setXxx方法進行注入
injectExtension(instance);
//Wrapper的包裝
//cachedWrapperClasses存放著所有的Wrapper類
//cachedWrapperClasses是在加載擴展實現類的時候放進去的
//Wrapper類的說明在最上面擴展點自動包裝(AOP)
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
//比如在包裝之前的instance是DubboProtocol實例
//先使用構造器來實例化當前的包裝類
//包裝類中就已經包含了我們的DubboProtocol實例
//然后對包裝類進行injectExtension注入,注入過程在上面
//最后返回的Instance就是包裝類的實例。
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
//這里返回的是經過所有的包裝類包裝之后的實例
return instance;
} catch (Throwable t) { }
}
獲取的Extension是經過層層包裝的擴展實現,然后就是調用經過包裝的refer方法了,這就到了具體的實現中的方法了。
到此為止調用refprotocol.refer(Class<T> type, URL url))
方法的過程也解析完了。
關于getActivateExtension方法的解析,等下再添加。