關於dubbo擴展點的一點分析


擴展點能力

  1. 能load class,這個class除了頂層接口class(在ExtensionLoader中對應type字段),還能load各實現類的class。
  2. 能創建instance。
  3. 能指定這個頂層接口的默認實現類的beanName。做法參見SPI注解部分。
  4. 能把創建出來的instance的字段注入。set開頭的且有一個參數且是public的,注入。
  5. 能adaptive。根據url上對該接口配置的實現類,將該接口的事情交給這個實現類去做(我更多的理解成委托)。此能力采用代碼生成再編譯的方式。代碼生成示例可以參見adaptive類代碼示例。 adaptive只會生成一個adaptive實現類。生成代碼的邏輯在com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClassCode()
  6. 能wrapper。wrapper是指這個頂層接口的實現類的構造函數的入參是這個頂層接口類型。那么這個實現類稱之為wrapper類,可以有多個,構建instance時不分多個之間的順序。因為用來給構造函數傳參的instance是這個頂層類的默認實現。比如com.alibaba.dubbo.rpc.Protocol接口,有實現類 com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol beanName是injvm,QosProtocolWrapper、ProtocolListenerWrapper、ProtocolFilterWrapper是其三個wrapper類。創建warpper instance的代碼邏輯在com.alibaba.dubbo.common.extension.ExtensionLoader.createExtension(String)中。
  7. 能active。實現類加了Activate注解的。在ExtensionLoader.getActivateExtension時會根據當前的url(配置信息)中值來匹配Activate注解中指定的值是否能match,能match的表示是activate,意思是命中的。在過濾器擴展點中用到。比如這個過濾器是給CONSUMER group用。示例有ExceptionFilter等。同時該注解還能支持order屬性來定義bean的順序。

擴展點使用

配置文件相關

配置文件放在哪里?

  1. META-INF/dubbo/internal/配置文件
  2. META-INF/dubbo/配置文件
  3. META-INF/services/配置文件

配置文件名是頂層接口全限定名,比如:com.alibaba.dubbo.rpc.Protocol

配置文件中內容:
一行是一個實現類的定義,大致是
beanName=實現類的class的全限定名,這個后面還可以接上#xxx(這個能力實際使用少)。beanName=這部分不是必須的。可以僅僅寫實現類的全限定名。

注解相關

SPI

SPI注解用在頂層接口上,其值表示這個接口的默認實現類的beanName,也即是說指定一個頂層接口的默認實現通過SPI注解加載頂層接口上指定即可。

Adaptive

Adaptive注解用在頂層接口或者接口的方法上,其表示這個接口或者這個方法需要有adaptive的類委托完成,未加注解的會生成不支持的操作的方式實現。

Activate

Activate注解用在實現類上,其表示實現類在rpc時根據url參數中以及注解中指定的key 目標value是否能匹配來決定此bean是否被選中(一般用在過濾器的命中判斷上)。

生成的代碼

adaptive類代碼示例

package com.alibaba.dubbo.registry;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
	public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
		if (arg0 == null)
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg0;
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException(
					"Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString()
							+ ") use keys([protocol])");
		com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory) ExtensionLoader
				.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
		return extension.getRegistry(arg0);
	}
}
package com.alibaba.dubbo.rpc.cluster;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
	public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0)
			throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
		if (arg0.getUrl() == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
		com.alibaba.dubbo.common.URL url = arg0.getUrl();
		String extName = url.getParameter("cluster", "failover");
		if (extName == null)
			throw new IllegalStateException(
					"Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString()
							+ ") use keys([cluster])");
		com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader
				.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
		return extension.join(arg0);
	}
}

雜項

查找所有的dubbo擴展點形式的配置文件

find ./ -type f -name "com.alibaba.dubbo*"|grep -v "/target/"|grep -v "/bin/"|grep -v "/test/"

另:
關於代理模式,dubbo未實現通用的,只是rpc語義實現里rpc調用的代理,借助擴展點機器加動態代理完成。
具體其頂層接口是com.alibaba.dubbo.rpc.ProxyFactory。用在比如將EchoService編織進每次RPC調用中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM