Dubbo啟動(dòng)源碼解析一

一店诗、啟動(dòng)入口

這次講 dubbo-spring-boot-starter 啟動(dòng)方式,所以入口就是Spring的SPI機(jī)制;
首先在META-INF/spring.factories配置下,配置了org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration類,在啟動(dòng)時(shí)丰辣,則會(huì)把DubboAutoConfiguration類注冊(cè)到spring容器中;
我們來看下DubboAutoConfiguration
先看啟動(dòng)流程

   @Bean
    public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(@Qualifier("dubboScanBasePackagesPropertyResolver") PropertyResolver propertyResolver) {
        Set<String> packagesToScan = (Set)propertyResolver.getProperty("base-packages", Set.class, Collections.emptySet());
        return new ServiceAnnotationBeanPostProcessor(packagesToScan);
    }

我們先看下生產(chǎn)者端的啟動(dòng)流程禽捆,首先是在Spring中注冊(cè)ServiceAnnotationBeanPostProcessor類

二笙什、ServiceAnnotationBeanPostProcessor解析

我們先看下ServiceAnnotationBeanPostProcessor類
ServiceAnnotationBeanPostProcessor

該類實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口,則在Spring容器初始化時(shí)胚想,會(huì)調(diào)用postProcessBeanDefinitionRegistry方法

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 注冊(cè)DubboBootstrapApplicationListener類
        registerBeans(registry, DubboBootstrapApplicationListener.class);
        //獲取掃描路徑
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            //根據(jù)掃描路徑去掃描生成實(shí)例并注冊(cè)琐凭,且生成相應(yīng)的ServiceBean對(duì)象,且注冊(cè)到Spring中
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }

我們會(huì)看到浊服,這個(gè)時(shí)候會(huì)去注冊(cè)DubboBootstrapApplicationListener類统屈,這個(gè)類我們等流程到了在分析,我們先按啟動(dòng)流程看過去牙躺;resolvePackagesToScan方法先獲取到需要掃描的包 愁憔,然后再調(diào)用registerServiceBeans去注冊(cè)相關(guān)實(shí)例,我們重點(diǎn)來看下registerServiceBeans方法

    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //new出dubbo的掃描器孽拷,主要是繼承ClassPathBeanDefinitionScanner吨掌,功能幾乎一樣,只是多了一些獲取環(huán)境參數(shù)的功能
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
        scanner.setBeanNameGenerator(beanNameGenerator);
        //添加需要掃描的注解(主要是為了兼容以前的版本,還有會(huì)掃描alibaba這個(gè)包下的)
        scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
        scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
        for (String packageToScan : packagesToScan) {

            // 掃描到的類膜宋,就會(huì)注冊(cè)進(jìn)spring容器
            scanner.scan(packageToScan);
            //獲取到剛剛掃描出的類
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                //掃描出的打有@Service注解的類窿侈,會(huì)挨個(gè)去生成ServiceBean去注冊(cè)
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }
                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                            beanDefinitionHolders +
                            " } were scanned under package[" + packageToScan + "]");
                }
            } else {
                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                            + packageToScan + "]");
                }
            }
        }
    }

接下來,我們主要去看下registerServiceBean方法

    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        Annotation service = findServiceAnnotation(beanClass);
        //獲取當(dāng)前對(duì)象的注解參數(shù)
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
        //獲取接口類型
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
        //構(gòu)建serviceBean bean定義
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
        // 生成ServiceBean在Spring容器中的名稱(ServiceBean:接口全限定名+分組+版本)
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
        //注冊(cè)ServiceBean
        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

            if (logger.isInfoEnabled()) {
                logger.info("The BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean has been registered with name : " + beanName);
            }
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean[ bean name : " + beanName +
                        "] was be found , Did @DubboComponentScan scan to same package in many times?");
            }
        }
    }

接下來秋茫,我們來看下buildServiceBeanDefinition方法

    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                              AnnotationAttributes serviceAnnotationAttributes,
                                                              Class<?> interfaceClass,
                                                              String annotatedServiceBeanName) {
        //生成ServiceBean bean定義對(duì)象
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        //排除不需要設(shè)置的屬性
        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");
        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
        // 設(shè)置ref對(duì)象史简,addPropertyReference則會(huì)根據(jù)名稱在Spring容器中找到相應(yīng)的對(duì)象注入進(jìn)去
        addPropertyReference(builder, "ref", annotatedServiceBeanName);
        // 以下都是設(shè)置ServiceBean 屬性,主要是從@Service注解參數(shù)中獲取
        builder.addPropertyValue("interface", interfaceClass.getName());
        // Convert parameters into map
        builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
        // Add methods parameters
        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
        if (!methodConfigs.isEmpty()) {
            builder.addPropertyValue("methods", methodConfigs);
        }
        /**
         * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
         */
        String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
        if (StringUtils.hasText(providerConfigBeanName)) {
            addPropertyReference(builder, "provider", providerConfigBeanName);
        }
        /**
         * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
         */
        String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
        if (StringUtils.hasText(monitorConfigBeanName)) {
            addPropertyReference(builder, "monitor", monitorConfigBeanName);
        }
        /**
         * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
         */
        String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
        if (StringUtils.hasText(applicationConfigBeanName)) {
            addPropertyReference(builder, "application", applicationConfigBeanName);
        }
        /**
         * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
         */
        String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
        if (StringUtils.hasText(moduleConfigBeanName)) {
            addPropertyReference(builder, "module", moduleConfigBeanName);
        }
        /**
         * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
         */
        String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
        List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
        if (!registryRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("registries", registryRuntimeBeanReferences);
        }
        /**
         * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
         */
        String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
        List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
        if (!protocolRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
        }
        return builder.getBeanDefinition();
    }

到這肛著,ServiceBean注冊(cè)成功圆兵,ServiceBean類很重要,每個(gè)Dubbo service實(shí)例都對(duì)應(yīng)一個(gè)ServiceBean策泣,相關(guān)配置都在ServiceBean中衙傀;我們?cè)倩氐介_始注冊(cè)的DubboBootstrapApplicationListener類

三、DubboBootstrapApplicationListener解析

DubboBootstrapApplicationListener

DubboBootstrapApplicationListener類繼承了OneTimeExecutionApplicationContextEventListener萨咕,OneTimeExecutionApplicationContextEventListener實(shí)現(xiàn)了ApplicationListener,主要監(jiān)聽了Spring容器生命周期火本,我們看下onApplicationContextEvent方法

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

我們可以看到危队,當(dāng)Spring容器啟動(dòng)成功時(shí),會(huì)調(diào)用dubboBootstrap.start()钙畔;

    public DubboBootstrap start() {
        if (started.compareAndSet(false, true)) {
            //初始化 DubboBootstrap 
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            // 1. 導(dǎo)出服務(wù)
            exportServices();
            // Not only provider register
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 導(dǎo)出服務(wù)元數(shù)據(jù)
                exportMetadataService();
                //注冊(cè)服務(wù)實(shí)例
                registerServiceInstance();
            }
            //消費(fèi)端引用服務(wù)
            referServices();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }

    private void exportServices() {
        configManager.getServices().forEach(sc -> {
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);

            if (exportAsync) {
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    //導(dǎo)出服務(wù)茫陆,調(diào)用每個(gè)注冊(cè)的ServiceBean的export方法去導(dǎo)出
                    sc.export();
                });
                asyncExportingFutures.add(future);
            } else {
                sc.export();
                exportedServices.add(sc);
            }
        });
    }

接下來,主要邏輯在ServiceBean中擎析,這個(gè)export方法在其父類ServiceConfig中簿盅,我們下一篇主要講ServiceConfig邏輯;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揍魂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝇棉,老刑警劉巖俐末,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異庄蹋,居然都是意外死亡瞬内,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門限书,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虫蝶,“玉大人,你說我怎么就攤上這事倦西∧苷妫” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舟陆。 經(jīng)常有香客問我误澳,道長,這世上最難降的妖魔是什么秦躯? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任忆谓,我火速辦了婚禮,結(jié)果婚禮上踱承,老公的妹妹穿的比我還像新娘倡缠。我一直安慰自己,他們只是感情好茎活,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布昙沦。 她就那樣靜靜地躺著,像睡著了一般载荔。 火紅的嫁衣襯著肌膚如雪盾饮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天懒熙,我揣著相機(jī)與錄音丘损,去河邊找鬼。 笑死工扎,一個(gè)胖子當(dāng)著我的面吹牛徘钥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肢娘,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼呈础,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了橱健?” 一聲冷哼從身側(cè)響起而钞,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎畴博,沒想到半個(gè)月后笨忌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俱病,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年官疲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亮隙。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡途凫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溢吻,到底是詐尸還是另有隱情维费,我是刑警寧澤果元,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站犀盟,受9級(jí)特大地震影響而晒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阅畴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一倡怎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贱枣,春花似錦监署、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至春塌,卻和暖如春晓避,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背只壳。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工够滑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吕世。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像梯投,于是被迫代替她去往敵國和親命辖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容