dubbo源碼:配置文件加載自定義標籤解析

      最近由於項目須要準備看dubbo源碼,下載代碼以後發現工程有幾十個之多,第一次看dubbo源碼會有無從下手的感受。根據學習框架的通常步驟,先看文檔,在作demo,進行入門。java

      跑了demo以後,發現功能不少,實現比較複雜,由於不少信息都在配置文件中體現,感受能夠從配置入口。只要留心就會發現dubbo工程中又不少api工程,會發現dubbo抽象出來不少核心接口模塊,每一個接口模塊下面都會有一個或者多個實現方式。首先來看config工程源碼:dubbo-config-api和dubbo-config-spring。node

客戶端配置:spring

<dubbo:consumer cluster="failover" loadbalance="mysfpay" retries="1" timeout="60000"/>
<dubbo:application name="demo-consumer" />
<dubbo:registry address="zookeeper://10.118.242.90:2181"
		client="curator" group="china" />

<dubbo:annotation package="com.alibaba" />
	
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService"/>

服務端配置:api

<dubbo:application name="demo-provider" />
<dubbo:registry address="zookeeper://10.118.242.90:2181"
		client="curator" group="china" />
<!-- 多協議配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:annotation package="com.alibaba" />
<!-- 測試接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService"
		ref="demoService" timeout="6000" cache="lru"/>

從上面配置能夠發現(在生產者主要是配置要發佈出去service,而消費者主要是配置訂閱要使用的reference)在配置文件中的每個標籤均可以在dubbo-config-api中找到實體類相對應。app

      若是注意看這些配置類是存在一些層級關係的,將一些具備通用性的屬性定義在抽象類中,將每一個標籤所特有的屬性定義在子類中。框架

從上圖中能夠看到標籤:dubbo:reference、dubbo:consumer以及dubbo:service、dubbo:provider繼承自抽象類AbstractInterfaceConfig,而抽象類中配置有cluster(集羣方式)、proxy(代理類型)等屬性因此在上面的reference、consumer、service、provider標籤中也能夠配置cluster和proxy等屬性。dubbo-config-api工程中還有兩個註解類的定義@interface Reference、@interface Service因此在dubbo中發佈服務和訂閱服務也能夠經過配置來完成。到此咱們已經基本看完了dubbo-config-api工程中源碼,能夠發如今接口工程中主要實現的比較基礎通用的代碼,如配置屬性定義,接口定義。ide

public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {


    // 服務接口的本地實現類名
    protected String               local;

    // 服務接口的本地實現類名
    protected String               stub;

    // 服務監控
    protected MonitorConfig        monitor;
    
    // 代理類型
    protected String               proxy;
    
    // 集羣方式
    protected String               cluster;

    // 過濾器
    protected String               filter;
    
    // 監聽器
    protected String               listener;

    // 負責人
    protected String               owner;

    // 鏈接數限制,0表示共享鏈接,不然爲該服務獨享鏈接數
    protected Integer              connections;
    
    // 鏈接數限制
    protected String               layer;
    
    // 應用信息
    protected ApplicationConfig    application;
    
    // 模塊信息
    protected ModuleConfig         module;

    // 註冊中心
    protected List<RegistryConfig> registries;
    
    // callback實例個數限制
    private Integer                callbacks;
    
    // 鏈接事件
    protected String              onconnect;
    
    // 斷開事件
    protected String              ondisconnect;

    // 服務暴露或引用的scope,若是爲local,則表示只在當前JVM內查找.
	private String scope;
    //其餘代碼暫略...
}
public abstract class AbstractServiceConfig extends AbstractInterfaceConfig {

    // 服務版本
    protected String               version;

    // 服務分組
    protected String               group;

    // 服務是否已經deprecated
    protected Boolean              deprecated;

    // 延遲暴露
    protected Integer              delay;

    // 是否暴露
    protected Boolean              export;

    // 權重
    protected Integer              weight;

    // 應用文檔
    protected String               document;

    // 在註冊中心上註冊成動態的仍是靜態的服務
    protected Boolean              dynamic;

    // 是否使用令牌
    protected String               token;

    // 訪問日誌
    protected String               accesslog;

    // 容許執行請求數
    private Integer                executes;

    protected List<ProtocolConfig> protocols;

    // 是否註冊
    private Boolean                register;
}

 

public class ServiceConfig<T> extends AbstractServiceConfig {

    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    
    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

    private static final Map<String, Integer> RANDOM_PORT_MAP = new HashMap<String, Integer>();

    // 接口類型
    private String              interfaceName;

    private Class<?>            interfaceClass;

    // 接口實現類引用
    private T                   ref;

    // 服務名稱
    private String              path;

    // 方法配置
    private List<MethodConfig>  methods;

    private ProviderConfig provider;

    private final List<URL> urls = new ArrayList<URL>();
    
    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

    private transient volatile boolean exported;

	private transient volatile boolean unexported;
    
    private volatile String generic;
}

以上是service配置的繼承關係類,這裏只列出了屬性,reference配置的相關類本身能夠下載源碼查看。    學習

    接下來咱們繼續來看dubbo-config-spring工程,咱們都知道dubbo的配置擴展於spring的配置,在配置文件中咱們不只能夠配置spring標籤也能夠配置dubbo標籤,經過類DubboNamespaceHandler、DubboBeanDefinitionParser以及dubbo.xsd、spring.handlers、spring.schemas咱們能夠看出dubbo對配置文件的解析其實使用的是「spring自定義標籤的功能」。在dubbo.xsd文件中進行標籤屬性的定義,描述自定義組件內容,而後經過DubboBeanDefinitionParser實現了BeanDefinitionParser接口解析XSD文件中定義的屬性,最後經過DubboNamespaceHandler擴展自NamespaceHandlerSupport的handler將自定義組件註冊到spring容器中。簡單來講dubbo-config-spring工程的主要功能就是將dubbo自定標籤進行解析並註冊到spring容器中。ui

dubbo.xsd文件中service標籤描述內容以下this

<xsd:complexType name="serviceType">
		<xsd:complexContent>
			<xsd:extension base="abstractServiceType">
				<xsd:choice minOccurs="0" maxOccurs="unbounded">
					<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
					<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
					<xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
				</xsd:choice>
				<xsd:attribute name="interface" type="xsd:token" use="required">
					<xsd:annotation>
						<xsd:documentation><![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>
						<xsd:appinfo>
							<tool:annotation>
								<tool:expected-type type="java.lang.Class"/>
							</tool:annotation>
						</xsd:appinfo>
					</xsd:annotation>
				</xsd:attribute>
				<xsd:attribute name="ref" type="xsd:string" use="optional">
					<xsd:annotation>
						<xsd:documentation><![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>
					</xsd:annotation>
				</xsd:attribute>
				<xsd:attribute name="class" type="xsd:string" use="optional">
					<xsd:annotation>
						<xsd:documentation><![CDATA[ The service implementation class name. ]]></xsd:documentation>
					</xsd:annotation>
				</xsd:attribute>
				<xsd:attribute name="path" type="xsd:string" use="optional">
					<xsd:annotation>
						<xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>
					</xsd:annotation>
				</xsd:attribute>
				<xsd:attribute name="provider" type="xsd:string" use="optional">
					<xsd:annotation>
						<xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>
					</xsd:annotation>
				</xsd:attribute>
                <xsd:attribute name="generic" type="xsd:string" use="optional">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
				<xsd:anyAttribute namespace="##other" processContents="lax" />
			</xsd:extension>
		</xsd:complexContent>
	</xsd:complexType>

      咱們來看其中關鍵步驟的代碼,DubboNamespaceHandler類中定義的init方法,DubboBeanDefinitionParser解析器解析組件屬性到對應的bean中。

public void init() {
		//放入一個map中
		//兩個參很多天一個是節點名稱,第二個參數該節點解析的類
	    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }

對自定義標籤的具體解析實現代碼在DubboBeanDefinitionParser類中的parse方法中。該方法主要做用就是將配置文件中配置的組件,解析描述成一個spring容器中管理的bean對象(RootBeanDefinition),而後將該組件放入spring容器中進行管理。

private static BeanDefinition parse(Element element,
			ParserContext parserContext, Class<?> beanClass, boolean required) {
		//BeanDefinition是spring中定義的接口描述了一個bean的實例,包括屬性值,構造方法參數值和繼承自它的類的更多信息,
		//RootBeanDefinition實現了BeanDefinition接口,表示該bean是從配置源(配置文件等)加載生成的,
		RootBeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClass(beanClass);//設置bean類型
		beanDefinition.setLazyInit(false);//設置該bean加載方式
		String id = element.getAttribute("id");
		if ((id == null || id.length() == 0) && required) {
			String generatedBeanName = element.getAttribute("name");
			if (generatedBeanName == null || generatedBeanName.length() == 0) {
				if (ProtocolConfig.class.equals(beanClass)) {
					generatedBeanName = "dubbo";
				} else {
					generatedBeanName = element.getAttribute("interface");
				}
			}
			if (generatedBeanName == null || generatedBeanName.length() == 0) {
				generatedBeanName = beanClass.getName();
			}
			id = generatedBeanName;
			int counter = 2;
			while (parserContext.getRegistry().containsBeanDefinition(id)) {
				id = generatedBeanName + (counter++);
			}
		}
		if (id != null && id.length() > 0) {
			if (parserContext.getRegistry().containsBeanDefinition(id)) {
				throw new IllegalStateException("Duplicate spring bean id "
						+ id);
			}
			parserContext.getRegistry().registerBeanDefinition(id,
					beanDefinition);
			beanDefinition.getPropertyValues().addPropertyValue("id", id);
		}
		//解析爲Protocol組件時處理
		if (ProtocolConfig.class.equals(beanClass)) {
			for (String name : parserContext.getRegistry()
					.getBeanDefinitionNames()) {
				BeanDefinition definition = parserContext.getRegistry()
						.getBeanDefinition(name);
				PropertyValue property = definition.getPropertyValues()
						.getPropertyValue("protocol");
				if (property != null) {
					Object value = property.getValue();
					if (value instanceof ProtocolConfig
							&& id.equals(((ProtocolConfig) value).getName())) {
						definition.getPropertyValues().addPropertyValue(
								"protocol", new RuntimeBeanReference(id));
					}
				}
			}
		} else if (ServiceBean.class.equals(beanClass)) {//解析爲service時處理
			String className = element.getAttribute("class");
			if (className != null && className.length() > 0) {
				RootBeanDefinition classDefinition = new RootBeanDefinition();
				classDefinition.setBeanClass(ReflectUtils.forName(className));
				classDefinition.setLazyInit(false);
				parseProperties(element.getChildNodes(), classDefinition);
				beanDefinition.getPropertyValues().addPropertyValue("ref",
						new BeanDefinitionHolder(classDefinition, id + "Impl"));
			}
		} else if (ProviderConfig.class.equals(beanClass)) {//解析爲provider時處理
			parseNested(element, parserContext, ServiceBean.class, true,
					"service", "provider", id, beanDefinition);
		} else if (ConsumerConfig.class.equals(beanClass)) {//解析爲consumer時處理
			parseNested(element, parserContext, ReferenceBean.class, false,
					"reference", "consumer", id, beanDefinition);
		}
		Set<String> props = new HashSet<String>();
		ManagedMap parameters = null;
		//處理該類中的方法、屬性給定義的屬性賦值
		for (Method setter : beanClass.getMethods()) {
             ...
		}
		NamedNodeMap attributes = element.getAttributes();
		int len = attributes.getLength();
		for (int i = 0; i < len; i++) {
			Node node = attributes.item(i);
			String name = node.getLocalName();
			if (!props.contains(name)) {
				if (parameters == null) {
					parameters = new ManagedMap();
				}
				String value = node.getNodeValue();
				parameters.put(name, new TypedStringValue(value, String.class));
			}
		}
		if (parameters != null) {
			beanDefinition.getPropertyValues().addPropertyValue("parameters",
					parameters);
		}
		return beanDefinition;
	}

   從上面代碼咱們能夠看出dubbo將配置的service、reference放入spring容器中進行管理。

相關文章
相關標籤/搜索