Dubbo的Adaptive機制是什么?
在回答這個問題之前,我們先說說擴展和Dubbo的SPI機制。
評價一個軟件擴展性好不好,說的是軟件本身有沒有預留足夠的擴展點,讓用戶去自定義軟件的功能,讓軟件調用你的代碼,運行你的代碼邏輯。比如在Web開發中,常常會自定義一些Filter,在一個請求的前后做一些額外的處理。用Java的語言來講,擴展點就是接口(Interface),擴展就是接口實現(implement)。
那Dubbo的SPI是什么?Dubbo的SPI機制,就是擴展點加載機制。Dubbo允許用戶在配置文件中配置擴展,形式為:key=value,key為擴展的名稱,value是擴展類的全限定名。例如,一個擴展點是Animal,有3個實現,分別是Dog、Cat、Tiger,在配置文件中做如下配置:
dog=com.xxx.Dog
然后在代碼中調用:
Animal dog = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("dog");
即可獲取到Dog的實例。但是擴展名(dog)是寫死的,如果我想換成cat,需要寫成:
Animal cat = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");
寫死名稱很不靈活,在有些場景下,需要動態的獲取擴展,有時想要cat實例,有時需要dog實例。那怎么做呢,很簡單只要擴展名是一個變量,就可以實現動態獲取擴展了:
Animal animal = ExtensionLoader.getExtensionLoader(Animal.class).getExtension(animal);
Dubbo的Adaptive機制,正是利用URL這個參數,在運行過程中動態的加載擴展。具體為,@Adaptive注解定義從URL取哪個key,對應的value就是擴展的名稱:
URL url = arg.getUrl();
String extName = url.getParameter( "animal_type", "cat");
Animal extension = ExtensionLoader
.getExtensionLoader( Animal.class )
.getExtension( extName );
其中,animal_type是在注解@Adaptive中定義的,cat是默認的擴展名
Dubbo會為方法上標注有@Adaptive的擴展點,自動生成一個$Adaptive后綴的包裝類,包裝類中的邏輯,就是上述所說的從URL中獲取擴展名,再獲取擴展的過程。
最終,獲取adaptive擴展的代碼為:
Animal animal = ExtensionLoader.getExtensionLoader(Animal.class).getAdaptiveExtension();
下面具體看下Dubbo為我們生成的包裝類的代碼長什么樣?
以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個方法
}
在這里打個斷點,就能看到動態拼接的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機制。