今天看了一些博文,都是關於dubbo源碼解析方面的。以爲有必要記一下。java
問題1:spring 如何注入dubbo 的?或者說怎麼集成dubbo 的,或者說 dubbo啓動時怎麼啓動spring的?spring
一、首先想要實現 在spring 中 發揮某框架的功能,就必須將該框架注入到springBean 中。
二、dubbo 中 dubbo-container-spring 模塊,類 spirngContainer 裏面的start方法實現了這功能。
三、上述的start方法 由dubbo 的Main方法調用。。。
四、start方法執行時 會調用spring配置文件路徑。若是沒有配置Spring xml文件的路徑,會默認加載classpath/META-INF下的spring/*.xml文件。express
public void start() { String configPath = ConfigUtils.getProperty(SPRING_CONFIG); if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); context.start(); }
問題2:spring 是如何識別dubbo 標籤的?apache
一、首先要清楚,spring 原生的標籤 好比一些註解,aop的。bean 的。爲何都能認識?
是由於在命名空間中指定了對應的標籤好比aop的 xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop"指定這兩個,就可使用aop的相關標籤。緩存
二、dubbo 也是同樣(須要在spring配置文件中添加dubbo的命名空間)dubbo-config-spring包下的META-INF目錄下spring.handlers和spring.schemas文件兩個文件就是來處理命名空間和xsd。
spring 在識別命名空間時,使用的是NamespaceHandlerSupport抽象類和NamespaceHandler接口
而dubbo 的DubboNamespaceHandler 繼承了NamespaceHandlerSupport,會在初始化時,加載並識別dubbo標籤,啓動dubbo的Main 方法時就加載了上述兩個文件進而讓spring知道自定義了NamespaceHandlerSupport;app
DubboNamespaceHandler源碼框架
/* * Copyright 1999-2011 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.dubbo.config.spring.schema; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import com.alibaba.dubbo.common.Version; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.ModuleConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.AnnotationBean; import com.alibaba.dubbo.config.spring.ReferenceBean; import com.alibaba.dubbo.config.spring.ServiceBean; /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { 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)); } }
進入registerBeanDefinitionParser的方法,能夠看到他是NamespaceHandlerSupport 的方法。less
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }
第一個參數 是 元素名稱,就是告訴spring 我要解析注入這個標籤中的class 第二個參數DubboBeanDefinitionParser 實現了BeanDefinitionParser接口,而BeanDefinitionParser接口 是spring 將xml標籤解析成BeanDefinition對象的接口,因此DubboBeanDefinitionParser 就是將spring中dubbo 的標籤轉換成BeanDefinition對象,其元素名稱之後還要給Invoker 對象調用服務端方法使用;jvm
*若是是dubbo:protocol標籤,dubboh還會檢查全部已經包含protocol屬性的BeanDefinition且protocol屬性對應的值是ProtocolConfig對象的bean,將其屬性的protocol值設置成當前的bean引用:
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));ide
*若是是dubbo服務提供者的dubbo:service標籤,則還會設置ref屬性爲對應接口class的實現類bean:
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
問題3:怎麼注入的呢?
一、其實就是上面的DubboBeanDefinitionParser類,進行操做的,該類將dubbo:reference這種標籤解析成spring可以管理的BeanDefinition對象(我喜歡叫容器,這貨是一個map)
二、仔細看dubbo消費者 注入類型是ReferenceBean。其父類是ReferenceConfig 他的屬性:
private static finalProxyFactoryproxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); //接口類型 private String interfaceName; private Class<?> interfaceClass; //接口代理類引用 private transient volatileTref; private transient volatileInvoker<?>invoker;
可知,dubbo 將標籤轉化爲對象的時候使用的是動態代理,這點與spring 的IOC大不同,,IOC是反射原理。
ReferenceConfig實現了FactoryBean接口 調用get方法返回代理對象,其子類ReferenceBean 再調用getObject(就是FactoryBean的方法),獲取代理對象。
那麼 dubbo使用的 是jdk 仍是cgLib呢?
cgLib沒有在使用的行列,不知道緣由。但,jdk 和Javassist 兩種卻被使用,默認使用Javassist動態代理模式(聽說效率很高)。可自定義使用jdk代理模式(因這種動態代理僅限接口,使用範圍受限,且效率不高,不多用)。
以上是小弟 從網上看到並實際操做實踐+本身一些理解的(已經看了很多,但不敢寫。怕理解有誤)。先記下來。有不穩當的地方後期完善。
2018-11-22補充:
一、在注入和生成動態代理的時候使用Javassist 其實生成的是一個字節碼文件,保存在jvm緩存中,等待調用。
簡要說明。對於提供者。就是spring 將dubbo標籤經過ServiceConfig 解析成了ServiceBean,並生成好了一個invoker,該invoker放在exporter對象下,
exporter對象又放在exporters對象下,而後建立的nettyServer 放在了protocol(單例的)對象下的一個容器裏,這樣serviceBean 裏面就有了可用的invoker(接收consumer 請求,執行本身的invoke方法,以後反射出業務類impl,執行業務代碼,將結果經過netty通道返回),nettyServer,等待被調用。
注意:服務提供者在建立出invoker 後 執行業務類的時候使用的是反射技術。不是代理。消費者在執行時,使用代理對象執行invoke方法 才能實現遠程調用。