概念
dubbo spi是dubbo對JDK spi的升級届案,針對JDK spi的一些弱勢進(jìn)行了優(yōu)化楣颠,官網(wǎng)的介紹如下:
Dubbo 的擴展點加載從 JDK 標(biāo)準(zhǔn)的 SPI (Service Provider Interface) 擴展點發(fā)現(xiàn)機制加強而來童漩。
Dubbo 改進(jìn)了 JDK 標(biāo)準(zhǔn)的 SPI 的以下問題:
JDK 標(biāo)準(zhǔn)的 SPI 會一次性實例化擴展點所有實現(xiàn)矫膨,如果有擴展實現(xiàn)初始化很耗時,但如果沒用上也加載直奋,會很浪費資源施禾。
如果擴展點加載失敗弥搞,連擴展點的名稱都拿不到了牛隅。比如:JDK 標(biāo)準(zhǔn)的 ScriptEngine顾腊,通過 getName() 獲取腳本類型的名稱,但如果 RubyScriptEngine 因為所依賴的 jruby.jar 不存在梆惯,導(dǎo)致 RubyScriptEngine 類加載失敗吗垮,這個失敗原因被吃掉了,和 ruby 對應(yīng)不起來怯屉,當(dāng)用戶執(zhí)行 ruby 腳本>時锨络,會報不支持 ruby羡儿,而不是真正失敗的原因锁右。
增加了對擴展點 IoC 和 AOP 的支持,一個擴展點可以直接 setter 注入其它擴展點拂到。
附上鏈接:https://dubbo.incubator.apache.org/zh-cn/docs/dev/SPI.html
dubbo一方面用spi機制完成自己的默認(rèn)實現(xiàn)兄旬,同時允許開發(fā)者在不清楚dubbo代碼細(xì)節(jié)的同時根據(jù)自己需求對框架能力進(jìn)行拓展。模仿該設(shè)計方式悯森,可以讓很多流程性業(yè)務(wù)編碼變得非常簡潔瓢姻。
環(huán)境搭建
從來不想在搭環(huán)境上浪費太多時間音诈,用最簡單的方式,免得降低讀代碼的耐心褥傍,
不需要構(gòu)建spring應(yīng)用恍风,普通工程即可誓篱,在pom中添加依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.1</version>
</dependency>
dubbo版本使用2.6.6燕鸽,后面源碼介紹也是基于這個版本的啊研,使用redis注冊中心。
接口以及其實現(xiàn)類:
public interface DemoService {
String sayHello(String name);
}
public class DemoServiceImpl implements DemoService{
public String sayHello(String name) {
return "Hello " + name;
}
}
參考官方文檔削解,編寫測試代碼:
// 服務(wù)實現(xiàn)
DemoService demoService = new DemoServiceImpl();
// 當(dāng)前應(yīng)用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubbo-learn");
// 連接注冊中心配置
RegistryConfig registry = new RegistryConfig();
registry.setProtocol("redis");
registry.setAddress("127.0.0.1:6379");
// 服務(wù)提供者協(xié)議配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(12345);
protocol.setThreads(200);
// 服務(wù)提供者暴露服務(wù)配置
// 此實例很重氛驮,封裝了與注冊中心的連接济似,請自行緩存砰蠢,否則可能造成內(nèi)存和連接泄漏
ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
service.setApplication(application);
// 多個注冊中心可以用setRegistries()
service.setRegistry(registry);
// 多個協(xié)議可以用setProtocols()
service.setProtocol(protocol);
service.setInterface(DemoService.class);
service.setRef(demoService);
service.setVersion("1.0.0");
// service.setFilter("demoFilter");
// 暴露及注冊服務(wù)
service.export();
// 此實例很重,封裝了與注冊中心的連接以及與提供者的連接律杠,請自行緩存柜去,否則可能造成內(nèi)存和連接泄漏
ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
reference.setApplication(application);
// 多個注冊中心可以用setRegistries()
reference.setRegistry(registry);
reference.setInterface(DemoService.class);
reference.setVersion("1.0.0");
// 注意:此代理對象內(nèi)部封裝了所有通訊細(xì)節(jié),對象較重讼撒,請緩存復(fù)用
DemoService demoService1 = reference.get();
System.out.println(demoService1.sayHello("jules"));
啟動redis椿肩,就可以執(zhí)行測試代碼了豺谈,效果如圖
我們順便看下服務(wù)暴露以后茬末,redis中保存的服務(wù)信息
可以看到注冊信息在redis是以哈希表的方式保存丽惭,哈希表的key即服務(wù)URL信息
源碼
初始化
在閱讀dubbo代碼時责掏,經(jīng)撑韧看到如下實現(xiàn):
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
dubbo使用ExtensionLoader實現(xiàn)他的spi機制证芭,以上代碼會生成一個Protocol的動態(tài)代理實現(xiàn),由代理去執(zhí)行真實邏輯叫潦。
每個擴展點對應(yīng)一個ExtensionLoader矗蕊,getExtensionLoader(Protocol.class)實際為獲取Protocol.class的ExtensionLoader氢架,代碼如下:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
先從緩存中獲取达箍,獲取不到則創(chuàng)建并放入緩存中,以供下次使用硬纤,ExtensionLoader只有一個構(gòu)造方法:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
獲取ExtensionLoader以后筝家,執(zhí)行g(shù)etAdaptiveExtension()方法獲取動態(tài)生成的Protocol,我們閱讀getAdaptiveExtension的代碼邏輯:
public T getAdaptiveExtension() {
// 從緩存中讀取
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 檢查生成的時候是否出現(xiàn)異常
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
// 典型的單例生成方式腮鞍,兩次驗證
if (instance == null) {
try {
// 生成單例并且緩存起來
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
以上代碼可知移国,核心邏輯在createAdaptiveExtension中迹缀,進(jìn)一步閱讀:
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
代理類在getAdaptiveExtensionClass中生成蜜徽,然后newInstance生成對象注入到injectExtension方法,于是先閱讀Class的創(chuàng)建過程砚蓬,getAdaptiveExtensionClass的代碼為:
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses()篇幅比較長灰蛙,主要是在第一次啟用的時候傅事,加載所有的擴展信息蹭越,getExtensionClasses調(diào)用loadExtensionClasses,我們直接看loadExtensionClasses的代碼:
private Map<String, Class<?>> loadExtensionClasses() {
// 讀取擴展點上的value值驾霜,放入cachedDefaultName中
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
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));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
dubbo的spi注解有一個String的value對象粪糙,以上代碼會讀取value值并存入cachedDefaultName中忿项,可以看下Protocol的spi注解value給的值是dubbo城舞,申明了默認(rèn)的實現(xiàn)
然后家夺,連續(xù)調(diào)用了三個loadDirectory方法拉馋,三個DIRECTORY分別是:
- META-INF/dubbo/internal/
- META-INF/dubbo/
- META-INF/services/
其中META-INF/dubbo/internal/里面都是dubbo內(nèi)部的一些擴展實現(xiàn)煌茴,官網(wǎng)在介紹spi機制的時候約定的路徑是META-INF/dubbo/日川,理論上META-INF/services/也支持。
打開/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件合住,里面有dubbo實現(xiàn)的對擴展點Protocol的實現(xiàn):
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
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
Protocol的默認(rèn)實現(xiàn)類是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。
loadDirectory調(diào)用loadResource笨使,讀取/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件并且一行一行遍歷硫椰,獲取實現(xiàn)類的class對象,然后在loadResource中會調(diào)用loadClass方法:
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
// 處理Adaptive注解并且放到cachedAdaptiveClass中
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());
}
// 包裝類蹄胰,則放入wrappers中
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
// 未帶注解的實現(xiàn)類裕寨,放入extensionClasses和cachedNames中
clazz.getConstructor();
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
這里引出了兩個個非常重要的注解:Adaptive和Activate宾袜,具體作用我們后面再說驾窟,閱讀上面代碼的第一個if可以確認(rèn),dubbo只允許一個實現(xiàn)類上出現(xiàn)Adaptive注解月培,否則會出現(xiàn)IllegalStateException異常。
總結(jié)一下上面的代碼杉畜,遍歷/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件寻行,獲取到內(nèi)部所有的class對象,會遇到四種情況:
類別 | 處理 | 備注 |
---|---|---|
實現(xiàn)類并且?guī)Я薃daptive注解 | 放到cachedAdaptiveClass | 只允許有一個帶有該注解 |
實現(xiàn)類并且不帶Adaptive注解 | 放到cachedActivates和cachedNames | - |
實現(xiàn)類并且?guī)ctivate注解和Adaptive注解 | 放到cachedActivates和cachedNames的同時杆烁,放入cachedActivates | - |
包裝類 | 放到cachedWrapperClasses | - |
我們現(xiàn)在回到getAdaptiveExtensionClass方法简卧,避免去翻,在附上一次代碼:
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
通過閱讀析校,已經(jīng)了解getExtensionClasses方法主要是對擴展文件的解析和實現(xiàn)類的加載智玻,其實也是為我們后續(xù)的調(diào)用做好準(zhǔn)備芙代,加載需要做IO,好在每個ExtensionLoader只需要執(zhí)行一次具體的加載邏輯就行页滚,初始化的時候已經(jīng)準(zhǔn)備就緒了铺呵。
接下來也是核心部分片挂,也就是createAdaptiveExtensionClass方法乙漓,真正去創(chuàng)建Protocol代理實現(xiàn)類的方法:
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);
}
第一行調(diào)用createAdaptiveExtensionClassCode生成code,createAdaptiveExtensionClassCode方法篇幅很長,不再贅述了,功能是生成動態(tài)類的類編碼,Protocol會生成如下代碼:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
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!"
);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba
.dubbo.rpc.RpcException {
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.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.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);
}
}
可以看到,代理類實現(xiàn)擴展點Protocol的所有實現(xiàn),其中對export和refer進(jìn)行了實現(xiàn),而getDefaultPort和destroy會直接異常,原因是Protocol的export和refer方法帶有注解@Adaptive,在createAdaptiveExtensionClassCode的邏輯中,只對有@Adaptive修飾的方法進(jìn)行實現(xiàn),@Adaptive可以注解在方法或者類上,注解在方法上則會動態(tài)生成實現(xiàn)方法,在該方法中會去查找真實需要執(zhí)行邏輯的實現(xiàn)類(dubbo自帶的或者是開發(fā)者擴展的)糜俗。
在Protocol$Adaptive中通過
com.alibaba.dubbo.rpc.Protocol extension ExtensionLoader.getExtensionLoader(
com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
獲取到一個Protocol的實現(xiàn)類楔敌,然后調(diào)用這個實現(xiàn)類對應(yīng)的方法掏觉,那么我們可以判斷,只有在發(fā)生真實調(diào)用的時候羊娃,我們才可以判斷具體是執(zhí)行了哪個實現(xiàn)類的對應(yīng)方法。
最后,用對應(yīng)的classLoader實例化Protocol$Adaptive,初始化過程就完成了。
調(diào)用
接下來看下怎么使用這個代理類吧,在Dubbo官網(wǎng)提供的分層模型中寄纵,Protocol位于遠(yuǎn)程調(diào)用層恃鞋,主要封裝了RPC調(diào)用肴楷,其他幾個很重要的概念:Invoker泥张,Invoker也在這一層钞钙,勸退重災(zāi)區(qū)宝磨。我們以protocol的refer進(jìn)行探索窿祥,refer方法會生成Invoker墙歪,基于不同的協(xié)議會生成不同的Invoker浪漠。
可以直接在com.alibaba.dubbo.config.ReferenceConfig#createProxy中進(jìn)行斷點拌牲,Proxy初始化需要Invoker失驶,Invoker的生成邏輯入口在createProxy中眷蜓。
截取createProxy中的部分代碼
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // assemble URL from register center's configuration
List<URL> us = loadRegistries(false);
if (us != null && !us.isEmpty()) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// use AvailableCluster only when register's cluster is available
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // not a registry url
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
這邊啰嗦下,目前的版本中谒养,本地服務(wù)默認(rèn)就是本地調(diào)用了瞳购,也就是如果你的服務(wù)提供者和你的服務(wù)發(fā)起者都在本地,默認(rèn)是使用本地服務(wù)盏浇,以上代碼中isJvmRefer會等于true芽狗,我在自己閱讀的時候,一般直接回在debug的時候去修改isJvmRefer的值以生成遠(yuǎn)程調(diào)用的Invoker滴劲,這次的關(guān)注點不在Invoker顾复,我們關(guān)心的是refprotocol.refer(interfaceClass, url)在執(zhí)行的時候,是怎么找到對應(yīng)的實現(xiàn)類的萧芙。
由之前的分析得知乙嘀,這邊的refprotocol.refer其實是調(diào)用了Protocol$Adaptive.refer方法(我一直不知道怎么對動態(tài)生成的類進(jìn)行debug,是不是完全不行呢盟榴?)婴噩,代碼是:
public Invoker refer(Class arg0, URL arg1) throws RpcException {
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(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() +
") use keys([protocol])");
Protocol extension = (Protocol) ExtensionLoader.getExtensionLoader(
Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
這邊有兩行重要的代碼
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
根據(jù)URL信息(dubbo的協(xié)議都是存在url里面的)信息几莽,獲取到Protocol,如果是null站欺,就默認(rèn)使用dubbo,否則使用URL的配置磷账,翻回去看下META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件的內(nèi)容贾虽,每個extName其實就是對應(yīng)的Protocol的實現(xiàn)。
我們回到ExtensionLoader绰咽,開始閱讀getExtension的代碼
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
cachedInstances用來緩存對應(yīng)的實現(xiàn)取募,第一次進(jìn)來肯定是空的蟆技,dubbo只有在真正需要某個實現(xiàn)類的時候才去實例化它,所以不需要擔(dān)心會有不使用但是很耗時的類被實例化聊品。然后執(zhí)行createExtension(name)實例化擴展對象几苍。
private T createExtension(String name) {
// 從緩存中拿到對應(yīng)的Class,在加載配置文件的時候已經(jīng)加入緩存
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
實例化或者從緩存中拿到對應(yīng)的實例伸眶,injectExtension會進(jìn)行簡單的依賴注入厘贼,然后返回實例,這時我們返回的參是真實要去執(zhí)行refer方法的Protocol實現(xiàn)嘴秸,本地服務(wù)就是InjvmProtocol庇谆。
至此,ExtensionLoader初始化 到執(zhí)行這個流程就結(jié)束了串述。