最近由於項目須要準備看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容器中進行管理。