dubbo特性
其中的高度可擴(kuò)展能力就是針對(duì)Protocol愿吹、Transport、Serialization等被設(shè)計(jì)為擴(kuò)展點(diǎn)庇配。
擴(kuò)展機(jī)制SPI測(cè)試效果:
SPI的可擴(kuò)展機(jī)制的原理:
以Protocol為例宾濒,首先檢查Protocol類是否添加了@SPI注解,然后查找Protocol依賴包中是否含有key值是http的實(shí)現(xiàn)類鸠补。
@SPI注解
org.apache.dubbo.rpc.Protocol.class
Protocol協(xié)議類添加了@SPI注解,@SPI("dubbo")中的dubbo表示Protocol的默認(rèn)實(shí)現(xiàn)嘀掸。其中export和refer添加了@Adaptive注解紫岩,是在服務(wù)導(dǎo)出和服務(wù)引用使用的
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
http對(duì)應(yīng)的類HttpProtocol
在測(cè)試SPI項(xiàng)目中,由于只在依賴中添加了dubbo-rpc-http
所以只能獲取到\resources\META-INF\dubbo\internal目錄下的org.apache.dubbo.rpc.Protocol文件的http和http1對(duì)應(yīng)的值睬塌。
其中Adaptive注解表示自適應(yīng)泉蝌,Activate表示激活的
getExtensionLoader
獲取指定類型的擴(kuò)展類加載類對(duì)象
1、獲取ExtensionLoader對(duì)象
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionLoader
判斷type是空衫仑、不是個(gè)接口梨与、沒有實(shí)現(xiàn)@SPI注解直接返回
2、創(chuàng)建ExtensionLoader
根據(jù)type類型創(chuàng)建ExtensionLoader類
org.apache.dubbo.common.extension.ExtensionLoader#ExtensionLoader
ExtensionFactory是擴(kuò)展實(shí)例工廠文狱,也是SPI實(shí)現(xiàn)粥鞋。如果type不等于ExtensionFactory則objectFactory就是獲取ExtensionFactory接口的Adaptive實(shí)現(xiàn)類。
ExtensionFactory的實(shí)現(xiàn)類:
①瞄崇、SpiExtensionFactory SPI相關(guān)
②呻粹、SpringExtensionFactory 與spring相關(guān)的擴(kuò)展類工廠
③壕曼、AdaptiveExtensionFactory 決定是用SpiExtensionFactory或SpringExtensionFactory來獲取實(shí)例
3、getAdaptiveExtension
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
得到Adaptive擴(kuò)展示例并添加到cachedAdaptiveInstance緩存中
4等浊、createAdaptiveExtension
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension
5腮郊、getAdaptiveExtensionClass
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass
獲取當(dāng)前接口的所有擴(kuò)展類、如果某個(gè)接口沒有手動(dòng)指定一個(gè)Adaptive類筹燕,那么就自動(dòng)生成一個(gè)Adaptive類
6轧飞、getExtensionClasses
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses
加載
7、loadExtensionClasses
org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses
加載擴(kuò)展類的文件夾dir
①撒踪、DUBBO_INTERNAL_DIRECTORY = META-INF/dubbo/internal/
②过咬、DUBBO_DIRECTORY = META-INF/dubbo/
③、SERVICES_DIRECTORY = META-INF/services/
ExtensionFactory的擴(kuò)展類
8制妄、loadDirectory
org.apache.dubbo.common.extension.ExtensionLoader#loadDirectory
加載文件夾dir和type.getName()組成的文件中加載數(shù)據(jù)掸绞。
例如上面的測(cè)試代碼就是從\resources\META-INF\dubbo\internal目錄下的org.apache.dubbo.rpc.Protocol文件加載的
9、loadResource
org.apache.dubbo.common.extension.ExtensionLoader#loadResource
加載數(shù)據(jù)流
10耕捞、loadClass
org.apache.dubbo.common.extension.ExtensionLoader#loadClass
①衔掸、緩存Adaptive
把添加Adaptive注解的當(dāng)前接口實(shí)現(xiàn)類添加到cachedAdaptiveClass中
②、Wrapper類
org.apache.dubbo.common.extension.ExtensionLoader#isWrapperClass
判斷一個(gè)類是不是Wrapper類俺抽,根據(jù)構(gòu)造方法判斷敞映。構(gòu)造方法參數(shù)類型是當(dāng)前接口,返回true
③凌埂、緩存Activate類
org.apache.dubbo.common.extension.ExtensionLoader#cacheActivateClass
11驱显、createAdaptiveExtensionClass
所以在第五步⑤getAdaptiveExtensionClass方法得到Adaptive自適應(yīng)的擴(kuò)展類時(shí)诗芜,如果緩存中已經(jīng)存在則直接返回瞳抓,如果沒有則會(huì)創(chuàng)建一個(gè)
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass
12、得到getAdaptiveExtension
(與第三步對(duì)應(yīng))org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
13伏恐、ExtensionLoader
(與第一步對(duì)應(yīng))得到type=interface org.apache.dubbo.rpc.Protocol
new ExtensionLoader<T>(type)得到的ExtensionLoader
getExtension
獲取指定擴(kuò)展類extensionLoader.getExtension("http")
org.apache.dubbo.common.extension.ExtensionLoader#createExtension
1孩哑、獲取當(dāng)前接口的所有擴(kuò)展類
①、getExtensionClasses加載擴(kuò)展類
org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses
②翠桦、loadExtensionClasses從資源文件加載擴(kuò)展類
org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses
Dubbo的IOC
2横蜒、依賴注入。屬性注入销凑,通過setXxx方法
依賴注入
org.apache.dubbo.common.extension.ExtensionLoader#injectExtension
①丛晌、遍歷當(dāng)前實(shí)例的所有方法,這里注入是通過set方法注入的斗幼,所以在找set方法
②澎蛛、setter方法的參數(shù)類型pt
③、截取setter方法所對(duì)應(yīng)的屬性名property
property值是setXxx中的Xxx
④蜕窿、objectFactory工廠得到一個(gè)對(duì)象谋逻,會(huì)從Spring容器或通過SPI機(jī)制得到一個(gè)對(duì)象(Adaptive對(duì)象)
⑤呆馁、反射調(diào)用setter方法進(jìn)行注入
method.invoke把需要注入對(duì)象通過執(zhí)行setXxx方法設(shè)置到當(dāng)前instance對(duì)象的屬性值中(與spring中的Autowire效果相同)
Dubbo中的AOP
3、Wrapper實(shí)例進(jìn)行依賴注入
Wrapper實(shí)現(xiàn)了AOP的效果
從cachedWrapperClasses緩存中獲取Wrapper對(duì)當(dāng)前實(shí)例進(jìn)行包裹傳入的參數(shù)是instance對(duì)象毁兆,可一層層包裹
Adaptive
AdaptiveClass表示自適應(yīng)類浙滤,自適應(yīng)類可以通過getAdaptiveExtension獲取。只要在類上添加一個(gè)@Adaptive注解气堕,就表示當(dāng)前類是這個(gè)實(shí)現(xiàn)類接口的自適應(yīng)類纺腊,如果沒有添加了Adaptive注解的類,在我們獲取這個(gè)類的自適應(yīng)類時(shí)茎芭,就會(huì)通過createAdaptiveExtensionClass代碼自動(dòng)生成的一個(gè)自適應(yīng)類摹菠。
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
這里獲取的Protocol/$Adaptive自適應(yīng)類中,只有export導(dǎo)出服務(wù)和refer引入服務(wù)進(jìn)行了Adaptive代理骗爆。是因?yàn)檫@兩個(gè)方法添加了Adaptive注解次氨。通過refer和export的比較可以看出,并不是加了Adaptive注解的就一定可以Adaptive代理摘投,arg1表示資源URL煮寡,arg1不能為空也就是說必須包含資源URL對(duì)象(無論是直接傳入還是其類中存在getUrl方法可以獲取到url),其關(guān)鍵就是URL包含的key(extName)的傳入犀呼。
Activate
Activate注解包含group和value的區(qū)分幸撕,就是為了匹配得到適用于不同場景的擴(kuò)展類。
group組分為PROVIDER和CONSUMER外臂,表示該擴(kuò)展點(diǎn)能在服務(wù)提供者端坐儿,或消費(fèi)端使用。
value表示參數(shù)的鍵宋光,當(dāng)調(diào)用getActivateExtension方法來獲取擴(kuò)展類時(shí)貌矿,如果傳入的url中的參數(shù)的key中,包括value的值罪佳,那么則表示當(dāng)前url可以使用這個(gè)擴(kuò)展點(diǎn)逛漫。
總結(jié):
dubbo的高度可擴(kuò)展能力體現(xiàn)在SPI上,SPI的源碼實(shí)現(xiàn)機(jī)制就是通過ExtensionLoader獲取指定的實(shí)現(xiàn)擴(kuò)展類赘艳。
ExtensionLoader擴(kuò)展加載類具體的實(shí)現(xiàn)就是加載對(duì)應(yīng)的資源文件( 通過目錄META-INF/dubbo/internal/酌毡、META-INF/dubbo/、META-INF/dubbo/與接口名字拼接的文件)得到擴(kuò)展類集合蕾管。
Adaptive類的生成和作用枷踏,Adaptive表示一個(gè)Adaptive代理類,可以通過資源URL調(diào)用指定擴(kuò)展類
Activate表示擴(kuò)展類的使用范圍用group組和url參數(shù)的鍵是否含有value來判斷
如果需要自定義擴(kuò)展只要遵循這個(gè)規(guī)則就可以了掰曾。