Dubbo之SpringBoot启动源码详解

news2024/7/6 17:36:13

需要前置知识,了解spring源码,springboot自动加载机制等

DubboBootstrap启动

详细信息可看 学习Dubbo源码需要了解的基础内容源码详解

DubboBootstrap 启动所需要的信息

  1. 添加应用程序配置
  2. 添加注册中心配置
  3. 添加协议配置
  4. 添加服务配置
  5. 启动

SpringBoot启动Dubbo

@EnableDubbo注解 中引用了 @DubboComponentScan@EnableDubboConfig注解

@DubboComponentScan注解中引用了 @Import(DubboComponentScanRegistrar.class) 所以以DubboComponentScanRegistrar类为起点

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
    String[] value() default {};
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
}

DubboComponentScanRegistrar

实现自 ImportBeanDefinitionRegistrar 重写了 registerBeanDefinitions方法,在SpringBoot源码中会回调 registerBeanDefinitions方法,可见SpringBoot源码

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // initialize dubbo beans
    // 核心 初始化环境 包括注册监听器
    DubboSpringInitializer.initialize(registry);

    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
	// 注册ServiceBean 和dubbo启动信息
    registerServiceAnnotationPostProcessor(packagesToScan, registry);
}

DubboSpringInitializer.initialize(registry) =》 调用 initContext(context, registry, beanFactory) 初始化上下文

initContext(context, registry, beanFactory) =》调用 DubboBeanUtils.registerCommonBeans(registry);注册公共使用Bean对象

DubboBeanUtils.registerCommonBeans(registry);

static void registerCommonBeans(BeanDefinitionRegistry registry) {

    registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);

    registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);

    // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
    registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
        ReferenceAnnotationBeanPostProcessor.class);

    // TODO Whether DubboConfigAliasPostProcessor can be removed ?
    // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
    registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
        DubboConfigAliasPostProcessor.class);

    // register ApplicationListeners
    // 核心,注册了部署调度监听器和应用程序监听器 
    registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
    registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);

    // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
    registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
        DubboConfigDefaultPropertyValueBeanPostProcessor.class);

    // Dubbo config initializer
    registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);

    // register infra bean if not exists later
    registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}

注册服务配置信息SercviceBean

registerServiceAnnotationPostProcessor(packagesToScan, registry);

注册了 ServiceAnnotationPostProcessor

private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
	// 注册 ServiceAnnotationPostProcessor
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
    builder.addConstructorArgValue(packagesToScan);
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}

ServiceAnnotationPostProcessor

实现了BeanDefinitionRegistryPostProcessor,所以会调用postProcessBeanDefinitionRegistry方法

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    this.registry = registry;
    // 扫描Bean
    scanServiceBeans(resolvedPackagesToScan, registry);
}

scanServiceBeans(resolvedPackagesToScan, registry);

这个方法就包含了注册dubbo配置的bean的逻辑了,需要扫描自定义的注解来注册到spring的ioc容器中的套路大致都是相同的,和Mybatis一样,使用spring的扩展机制来扫描自定义的注解,然后注册为BeanDefinition,即可在Spring中注册为bean

private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    scanned = true;
    if (CollectionUtils.isEmpty(packagesToScan)) {
        if (logger.isWarnEnabled()) {
            logger.warn(CONFIG_NO_BEANS_SCANNED, "", "", "packagesToScan is empty , ServiceBean registry will be ignored!");
        }
        return;
    }

    // 创建扫描包类
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    scanner.setBeanNameGenerator(beanNameGenerator);
    // 添加有 @DubboService  @Service  的过滤条件 
    for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    }

    ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
    scanner.addExcludeFilter(scanExcludeFilter);

    for (String packageToScan : packagesToScan) {

        // avoid duplicated scans
        if (servicePackagesHolder.isPackageScanned(packageToScan)) {
            if (logger.isInfoEnabled()) {
                logger.info("Ignore package who has already bean scanned: " + packageToScan);
            }
            continue;
        }

        // Registers @Service Bean first
        // 包扫描,扫描当前包路径下所有类(因为没开 @Indexed注解 )
        scanner.scan(packageToScan);

        // 查找@Service的所有beandefinitionholder,无论@ComponentScan是否扫描  
        // 包装成 BeanDefinitionHolder 返回
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
            if (logger.isInfoEnabled()) {
                List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
                }
                logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);
            }

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                // 核心 核心 对 每一个包装的 beanDefinitionHolder 处理
                processScannedBeanDefinition(beanDefinitionHolder);
                servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
            }
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn(CONFIG_NO_ANNOTATIONS_FOUND,"No annotations were found on the class","","No class annotated by Dubbo @Service was found under package ["
                        + packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
            }
        }

        servicePackagesHolder.addScannedPackage(packageToScan);
    }
}

@DubboService注解的扫描使用原理

这里保证文档可读性,简单介绍,详情见 Dubbo注解 详细介绍

DubboClassPathBeanDefinitionScanner 扫描包类 实现自 ClassPathBeanDefinitionScanner

在 scanServiceBeans 方法中 通过以下代码添加了 DubboService Service 注解的过滤条件,从而才scan 扫描报的时候根据过滤条件寻找类,Scan扫描包去看Spring扫描包原理

private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
        // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
        DubboService.class,
        // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
        Service.class,
        // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
        com.alibaba.dubbo.config.annotation.Service.class
);
// 添加有 @DubboService  @Service  的过滤条件 
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
    scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
…… 
scanner.scan(packageToScan);

processScannedBeanDefinition

private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {

    Class<?> beanClass = resolveClass(beanDefinitionHolder);
    // 获取 @DubboService
    Annotation service = findServiceAnnotation(beanClass);
    
    // 获取配置在注解@DubboService上的所有属性
    Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
    // 根据注解信息和类信息解析出接口信息
    String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
    // 拿到 @DubboSerivce修饰的类名
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
    
    // ServiceBean Bean name
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
    // 构造了关于Servicebean的 BeanDefinition
    AbstractBeanDefinition serviceBeanDefinition =
        buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
    // 把 关于Servicebean的 BeanDefinition 添加到spring中
    registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);

}

img

buildServiceBeanDefinition

构建了一个BeanDefinition,设置他的BeanClass = ServiceBean, 每个接口都会生成一个BeanDefinition

到这里位置,就已经把所以扫描到@DubboService(包括兼容历史所需要的 @Service )注解的类,全部注册为了BeanDefinition,开始进行spring bean的初始化,开始初始化设置的ServiceBean

private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
                                                          String serviceInterface,
                                                          String refServiceBeanName) {
	// 创建 ServiceBean 的Builder
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
	
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
	// 设置ServiceBean需要的相关参数,例如  protocol,ref 等
    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

    String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
            "methods", "interfaceName", "parameters");
	…… …… ……
    return builder.getBeanDefinition();

}

ServiceBean初始化

初始化会调用 InitializingBean的 afterPropertiesSet 方法,详解见Dubbo前置知识

DubboDeployApplicationListener

在ServiceBean初始化后 DubboDeployApplicationListener 监听器监听了 ContextRefreshedEvent 时间,在spring初始化完成后 该监听器会被执行

onApplicationEvent监听事件

@Override
public void onApplicationEvent(ApplicationContextEvent event) {
    if (nullSafeEquals(applicationContext, event.getSource())) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
}

onContextRefreshedEvent

private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    ModuleDeployer deployer = moduleModel.getDeployer();
    Assert.notNull(deployer, "Module deployer is null");
    // start module
	// 核心 部署器启动
    Future future = deployer.start();

    // if the module does not start in background, await finish
    if (!deployer.isBackground()) {
        try {
            future.get();
        } catch (InterruptedException e) {
            logger.warn(CONFIG_FAILED_START_MODEL, "", "", "Interrupted while waiting for dubbo module start: " + e.getMessage());
        } catch (Exception e) {
            logger.warn(CONFIG_FAILED_START_MODEL, "", "", "An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
        }
    }
}

DefaultModuleDeployer#start

部署器启动

@Override
public Future start() throws IllegalStateException {
    // initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
    // 初始化部署
    applicationDeployer.initialize();

    return startSync();
}

DefaultApplicationDeployer#initialize

初始化,包括配置中心,应用程序配置,元数据配置等

对应DubboBootstrap 代码启动的加载

@Override
public void initialize() {
    if (initialized) {
        return;
    }
    // Ensure that the initialization is completed when concurrent calls
    synchronized (startLock) {
        if (initialized) {
            return;
        }
        // register shutdown hook
        registerShutdownHook();

        startConfigCenter();

        loadApplicationConfigs();

        initModuleDeployers();

        // @since 2.7.8
        startMetadataCenter();

        initialized = true;

        if (logger.isInfoEnabled()) {
            logger.info(getIdentifier() + " has been initialized!");
        }
    }
}

DefaultModuleDeployer#startSync()

private synchronized Future startSync() throws IllegalStateException {
    if (isStopping() || isStopped() || isFailed()) {
        throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again");
    }

    try {
        if (isStarting() || isStarted()) {
            return startFuture;
        }
    	// 却换模块启动状态 STARTING
        onModuleStarting();

    	//如果为初始化则初始化
        initialize();

        // export services
        //服务暴漏
        exportServices();

        // prepare application instance
        // exclude internal module to avoid wait itself
        if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {
            applicationDeployer.prepareInternalModule();
        }

        // refer services
        //引用服务
        referServices();

        // if no async export/refer services, just set started
        //非异步启动直接转换状态为STARTED
        if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
            onModuleStarted();
        } else {
            //如果是异步启动,等待服务发布和服务引用的回调
            frameworkExecutorRepository.getSharedExecutor().submit(() -> {
                try {
                    // wait for export finish
                    waitExportFinish();
                    // wait for refer finish
                    waitReferFinish();
                } catch (Throwable e) {
                    logger.warn(CONFIG_FAILED_WAIT_EXPORT_REFER, "", "", "wait for export/refer services occurred an exception", e);
                } finally {
                    onModuleStarted();
                }
            });
        }
    } catch (Throwable e) {
        onModuleFailed(getIdentifier() + " start failed: " + e, e);
        throw e;
    }
    return startFuture;
}

服务暴漏和服务拉去见 源码详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

广东MES系统实施过程中的要点和难点

MES系统已经成为企业目前实施的焦点。但是MES系统又分为很多的种类&#xff0c;对企业之间则是很难选择的&#xff0c;因为大部分的企业对MES系统的要点和难点并不清楚&#xff0c;而今天就让先达盈致的小编带大家了解一下广东MES系统实施过程中的要点和难点。MES系统是实现企业…

戴尔T5810电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。硬件型号驱动情况主板戴尔T5810&#xff0c;C610/612芯片处理器英特尔至强E5-2620 v3已驱动内存12 GB已驱动硬盘500GB WD Blue Solid State Drive & 2TB Seagate Mobile Hard Drive (Upgraded)已驱动显卡RX 570 4Gb已驱…

october-cms

环境准备 靶机链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;3e4s 虚拟机网络链接模式&#xff1a;桥接模式 攻击机系统&#xff1a;kali linux 2021.1 信息收集 1.探测目标靶机ip。 2.探测靶机开放端口和服务情况。 漏洞探测 1.访问网页 2.用dirsearch扫描…

用javascript分类刷leetcode15.链表(图文视频讲解)

链表操作如下图&#xff1a; 动画过大&#xff0c;点击查看 时间复杂度&#xff1a; prepend: O(1)append: 如果已知尾节点O(1)&#xff0c;否则需要遍历到尾节点&#xff0c;然后加入新节点O(n)insert: 插入到已知节点的后面O(1)&#xff0c;需要先查找后插入O(n)lookup: O…

【STM32笔记】__WFI();进入不了休眠的可能原因

【STM32笔记】__WFI();进入不了休眠的可能原因 【STM32笔记】低功耗模式配置及避坑汇总 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置&#xff08;ADC唤醒无法使用、低功耗模式无法烧录解决方案&#xff09;…

Outlook邮箱注册教程 不信你看完还不懂

Outlook作为Microsoft Office家族的办公软件套装之一&#xff0c;关联着很多微软的其他产品。而且Outlook是欧美地区认可度比较高的&#xff0c;不仅可以用于一些境外联络还可以拿来注册Instagram、Twitter、Facebook等各种社交媒体平台。龙哥在这里就给大家出一份详细的Outloo…

Python打包调试问题解决

使用pyinstaller打包&#xff0c;发现问题&#xff1a;代码运行时调试的结果不一致代码中设定的图标打包后没有显示出来打包代码程序test.py为入口函数main&#xff08;&#xff09;所在的文件pyinstaller -F -w -i test.ico test.py 不会出现控制台&#xff0c;图标为test.ic…

电源大事,阻抗二字

作者&#xff1a;一博科技高速先生成员 姜杰PCB设计时&#xff0c;我们通常会控制走线的特征阻抗&#xff1b;电源设计时&#xff0c;又会关注电源分配系统&#xff08;PDN&#xff09;的交流阻抗&#xff0c;虽然都是阻抗&#xff0c;一个是信号的通道要求&#xff0c;一个是电…

电子标签拣货系统——外接供电版

Power_DC24v 型号&#xff1a;Power_DC24v24V电源适配器级联线&#xff1a;长30cm直径&#xff1a;15mmCK_Wire_V1 型号&#xff1a;CK_Wire_V1连接电源适配器级联线&#xff1a;长30cm公线&#xff1a;长宽厚 14*11*9mm母线&#xff1a;长宽厚 13*5.5*3mmCK_Wire_V2 型号&…

安卓玩机搞机-----没有第三方包 刷写第三方各种GSI系统 体验非官方系统

很多安卓友友热衷与刷这些各种第三方包体验。但有个别机型第三方资源较少。而且有的机型要体验非官方系统却没有对应系统的第三方包。那可以体验下刷gsi系统来畅玩。今天的帖子就聊聊GSI系统的各种刷写操作和对应的故障解析、 &#x1f494;&#x1f494;&#x1f494;&#x…

spring cloud(二)----------------Eureka注册中心环境搭建

一、首先创建一个没有架骨的maven主项目 点击下一步创建 创建完成后在maven主项目下删除src并且创建四个maven副项目分别叫&#xff1a; demospringcloud-api--------连接实例 demospringcloud-cosumer---------消费类 demospringcloud-eureka---------注册类 demospringc…

机器学习:基于支持向量机(SVM)进行人脸识别预测

机器学习&#xff1a;基于支持向量机&#xff08;SVM&#xff09;进行人脸识别预测 文章目录机器学习&#xff1a;基于支持向量机&#xff08;SVM&#xff09;进行人脸识别预测一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.准备数据2.业务理解3.数据理解4.数…

java3月train笔记

java笔记 day01 一、jdk和idea下载及安装&#xff08;一般不建议装C盘&#xff09;&#xff1a; jdk&#xff1a;java开发环境 idea&#xff1a;开发工具&#xff08;软件&#xff09;&#xff0c;用来编写代码的 苍老师文档服务器&#xff1a;doc.canglaoshi.org jdk下载&…

JavaEE|网络编程之套接字 TCP

文章目录一、ServerSocket API构造方法常用方法二、Socket API构造方法常用方法注意事项三、TCP中的长短连接E1:一发一收&#xff08;短连接&#xff09;E2:请求响应&#xff08;短连接&#xff09;E3&#xff1a;多线程下的TCP回响服务器说明&#xff1a;这部分说实话有点懵&a…

SrpingBoot拦截器

一、拦截器原理 根据当前请求&#xff0c;进入到 HandlerExecutionChain(可以处理请求的 handler 以及 handler 的所有拦截器)根据顺序执行所有拦截器的 preHandle() 方法如果当前拦截器的 preHandler() 方法返回 true&#xff0c;则执行下一个拦截器的 preHandler() 方法如果当…

Java常用框架(一)

思维导图 常见知识点 一、SpringBoot 1.简单介绍一下Spring及其优缺点 1.1 概念 重量级企业开发框架EJB的替代品&#xff0c;通过依赖注入、面向切面编程&#xff0c;使用简单Java对象POJO为企业Java开发提供了相对简单的方法。 1.2 优缺点 1.2.1 优点 组件代码轻量级 …

高并发架构 第一章大型网站数据演化——核心解释与说明。大型网站技术架构——核心原理与案例分析

大型网站架构烟花发展历程1.1.1初始阶段的网站构架1.1.2应用服务和数据服务分离1.1.3使用缓存改善网络性能1.1.4使用应用服务器集群改善网站的并发处理能力1.1.5数据库读写分离1.1.6使用反向代理和cdn加速网站相应1.1.1初始阶段的网站构架 大型网站都是由小型网站一步步发展而…

音视频基础之音频常见名词

采样频率 每秒钟采样的点的个数。常用的采样频率有&#xff1a; 22000&#xff08;22kHz&#xff09;&#xff1a; 无线广播。 44100&#xff08;44.1kHz&#xff09;&#xff1a;CD音质。 48000&#xff08;48kHz&#xff09;&#xff1a; 数字电视&#xff0c;DVD。 96000&am…

【C++提高编程】C++全栈体系(二十二)

C提高编程 第三章 STL - 常用容器 五、stack容器 1. stack 基本概念 概念&#xff1a;stack是一种先进后出(First In Last Out,FILO)的数据结构&#xff0c;它只有一个出口 栈中只有顶端的元素才可以被外界使用&#xff0c;因此栈不允许有遍历行为 栈中进入数据称为 — 入…

startForegroundService与startService 使用浅析

一. 了解服务&#xff08;Service&#xff09;的概念 service是安卓开发中一个很重要组件&#xff0c;意为“服务”。与我们常见的activity不同&#xff0c;“服务”是默默的在背后进行工作的&#xff0c;通常&#xff0c;它用于在后台为我们执行一些耗时&#xff0c;或者需要…