Dubbo的Adaptive機(jī)制是什么?
在回答這個(gè)問題之前雁竞,我們先說說擴(kuò)展和Dubbo的SPI機(jī)制钦椭。
評(píng)價(jià)一個(gè)軟件擴(kuò)展性好不好,說的是軟件本身有沒有預(yù)留足夠的擴(kuò)展點(diǎn)碑诉,讓用戶去自定義軟件的功能彪腔,讓軟件調(diào)用你的代碼,運(yùn)行你的代碼邏輯进栽。比如在Web開發(fā)中德挣,常常會(huì)自定義一些Filter,在一個(gè)請(qǐng)求的前后做一些額外的處理快毛。用Java的語言來講格嗅,擴(kuò)展點(diǎn)就是接口(Interface)番挺,擴(kuò)展就是接口實(shí)現(xiàn)(implement)。
那Dubbo的SPI是什么屯掖?Dubbo的SPI機(jī)制玄柏,就是擴(kuò)展點(diǎn)加載機(jī)制。Dubbo允許用戶在配置文件中配置擴(kuò)展贴铜,形式為:key=value粪摘,key為擴(kuò)展的名稱,value是擴(kuò)展類的全限定名阀湿。例如赶熟,一個(gè)擴(kuò)展點(diǎn)是Animal,有3個(gè)實(shí)現(xiàn)陷嘴,分別是Dog映砖、Cat、Tiger灾挨,在配置文件中做如下配置:
dog=com.xxx.Dog
然后在代碼中調(diào)用:
Animal dog = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("dog");
即可獲取到Dog的實(shí)例邑退。但是擴(kuò)展名(dog)是寫死的,如果我想換成cat劳澄,需要寫成:
Animal cat = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");
寫死名稱很不靈活地技,在有些場(chǎng)景下,需要?jiǎng)討B(tài)的獲取擴(kuò)展秒拔,有時(shí)想要cat實(shí)例莫矗,有時(shí)需要dog實(shí)例。那怎么做呢砂缩,很簡(jiǎn)單只要擴(kuò)展名是一個(gè)變量作谚,就可以實(shí)現(xiàn)動(dòng)態(tài)獲取擴(kuò)展了:
Animal animal = ExtensionLoader.getExtensionLoader(Animal.class).getExtension(animal);
Dubbo的Adaptive機(jī)制,正是利用URL這個(gè)參數(shù)庵芭,在運(yùn)行過程中動(dòng)態(tài)的加載擴(kuò)展妹懒。具體為,@Adaptive注解定義從URL取哪個(gè)key双吆,對(duì)應(yīng)的value就是擴(kuò)展的名稱:
URL url = arg.getUrl();
String extName = url.getParameter( "animal_type", "cat");
Animal extension = ExtensionLoader
.getExtensionLoader( Animal.class )
.getExtension( extName );
其中眨唬,animal_type是在注解@Adaptive中定義的,cat是默認(rèn)的擴(kuò)展名
Dubbo會(huì)為方法上標(biāo)注有@Adaptive的擴(kuò)展點(diǎn)好乐,自動(dòng)生成一個(gè)$Adaptive后綴的包裝類匾竿,包裝類中的邏輯,就是上述所說的從URL中獲取擴(kuò)展名曹宴,再獲取擴(kuò)展的過程搂橙。
最終,獲取adaptive擴(kuò)展的代碼為:
Animal animal = ExtensionLoader.getExtensionLoader(Animal.class).getAdaptiveExtension();
下面具體看下Dubbo為我們生成的包裝類的代碼長(zhǎng)什么樣?
以ProxyFactory為例区转,生成的ProxyFactory$Adaptive類的代碼如下:
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy( com.alibaba.dubbo.rpc.Invoker arg0 ) throws com.alibaba.dubbo.rpc.RpcException
{
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.getParameter( "proxy", "javassist" );
if ( extName == null )
throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])" );
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
.getExtensionLoader( com.alibaba.dubbo.rpc.ProxyFactory.class )
.getExtension( extName );
return (extension.getProxy( arg0 ) );
}
// 省略其他2個(gè)方法
}
在這里打個(gè)斷點(diǎn)苔巨,就能看到動(dòng)態(tài)拼接的adaptive包裝類的代碼:com.alibaba.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass
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);
}
下一篇,我們將從源碼的角度废离,分析Adaptive機(jī)制侄泽。