1. 程式人生 > >dubbo原始碼理解(1)啟動初始化與bean載入

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方法 才能實現遠端呼叫。