dubbo原始碼理解(1)啟動初始化與bean載入
今天看了一些博文,都是關於dubbo原始碼解析方面的。覺得有必要記一下。
問題1:spring 如何注入dubbo 的?或者說怎麼整合dubbo 的,或者說 dubbo啟動時怎麼啟動spring的?
1、首先想要實現 在spring 中 發揮某框架的功能,就必須將該框架注入到springBean 中。
2、dubbo 中 dubbo-container-spring 模組,類 spirngContainer 裡面的start方法實現了這功能。
3、上述的start方法 由dubbo 的Main方法呼叫。。。
4、start方法執行時 會呼叫spring配置檔案路徑。如果沒有配置Spring xml檔案的路徑,會預設載入classpath/META-INF下的spring/*.xml檔案。
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 標籤的?
1、首先要清楚,spring 原生的標籤 比如一些註解,aop的。bean 的。為什麼都能認識?
是因為在名稱空間中指定了對應的標籤比如aop的 xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop"指定這兩個,就可以使用aop的相關標籤。
2、dubbo 也是一樣(需要在spring配置檔案中新增dubbo的名稱空間)dubbo-config-spring包下的META-INF目錄下spring.handlers和spring.schemas檔案兩個檔案就是來處理名稱空間和xsd。
spring 在識別名稱空間時,使用的是NamespaceHandlerSupport抽象類和NamespaceHandler介面
而dubbo 的DubboNamespaceHandler 繼承了NamespaceHandlerSupport,會在初始化時,載入並識別dubbo標籤,啟動dubbo的Main 方法時就載入了上述兩個檔案進而讓spring知道自定義了NamespaceHandlerSupport;
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 的方法。
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 物件呼叫服務端方法使用;
*如果是dubbo:protocol標籤,dubboh還會檢查所有已經包含protocol屬性的BeanDefinition且protocol屬性對應的值是ProtocolConfig物件的bean,將其屬性的protocol值設定成當前的bean引用:
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
*如果是dubbo服務提供者的dubbo:service標籤,則還會設定ref屬性為對應介面class的實現類bean:
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
問題3:怎麼注入的呢?
1、其實就是上面的DubboBeanDefinitionParser類,進行操作的,該類將dubbo:reference這種標籤解析成spring能夠管理的BeanDefinition物件(我喜歡叫容器,這貨是一個map)
2、仔細看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補充:
1、在注入和生成動態代理的時候使用Javassist 其實生成的是一個位元組碼檔案,儲存在jvm快取中,等待呼叫。
簡要說明。對於提供者。就是spring 將dubbo標籤通過ServiceConfig 解析成了ServiceBean,並生成好了一個invoker,該invoker放在exporter物件下,
exporter物件又放在exporters物件下,然後建立的nettyServer 放在了protocol(單例的)物件下的一個容器裡,這樣serviceBean 裡面就有了可用的invoker(接收consumer 請求,執行自己的invoke方法,之後反射出業務類impl,執行業務程式碼,將結果通過netty通道返回),nettyServer,等待被呼叫。
注意:服務提供者在創建出invoker 後 執行業務類的時候使用的是反射技術。不是代理。消費者在執行時,使用代理物件執行invoke方法 才能實現遠端呼叫。