Dubbo之SpringBoot启动源码详解

news/2024/4/26 11:10:31/文章来源:https://blog.csdn.net/weixin_43988251/article/details/129125844

需要前置知识,了解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 BeanregisterInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,ReferenceAnnotationBeanPostProcessor.class);// TODO Whether DubboConfigAliasPostProcessor can be removed ?// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093registerInfrastructureBean(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 BeanregisterInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,DubboConfigDefaultPropertyValueBeanPostProcessor.class);// Dubbo config initializerregisterInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);// register infra bean if not exists laterregisterInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}

注册服务配置信息SercviceBean

registerServiceAnnotationPostProcessor(packagesToScan, registry);

注册了 ServiceAnnotationPostProcessor

private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {// 注册 ServiceAnnotationPostProcessorBeanDefinitionBuilder 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;// 扫描BeanscanServiceBeans(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 scansif (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/6007DubboService.class,// @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.ServiceService.class,// @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330com.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);// 获取 @DubboServiceAnnotation service = findServiceAnnotation(beanClass);// 获取配置在注解@DubboService上的所有属性Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);// 根据注解信息和类信息解析出接口信息String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);// 拿到 @DubboSerivce修饰的类名String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();// ServiceBean Bean nameString beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);// 构造了关于Servicebean的 BeanDefinitionAbstractBeanDefinition 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 的BuilderBeanDefinitionBuilder 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 finishif (!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 callssynchronized (startLock) {if (initialized) {return;}// register shutdown hookregisterShutdownHook();startConfigCenter();loadApplicationConfigs();initModuleDeployers();// @since 2.7.8startMetadataCenter();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;}// 却换模块启动状态 STARTINGonModuleStarting();//如果为初始化则初始化initialize();// export services//服务暴漏exportServices();// prepare application instance// exclude internal module to avoid wait itselfif (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {applicationDeployer.prepareInternalModule();}// refer services//引用服务referServices();// if no async export/refer services, just set started//非异步启动直接转换状态为STARTEDif (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {onModuleStarted();} else {//如果是异步启动,等待服务发布和服务引用的回调frameworkExecutorRepository.getSharedExecutor().submit(() -> {try {// wait for export finishwaitExportFinish();// wait for refer finishwaitReferFinish();} 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.luyixian.cn/news_show_71633.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

音视频基础之音频编码原理简介

一&#xff1a;隐蔽信号 数字音频信号如果不加压缩地直接进行传送&#xff0c;将会占用极大的带宽。例如&#xff0c;一套双声道数字音频若取样频率为44.1KHz&#xff0c;每样值按16bit量化&#xff0c;则其码率为&#xff1a; 244.1kHz16bit1.411Mbit/s 如此大的带宽将给信号…

电商数据查询平台:母婴行业妈妈用品全网热销,头部品牌格局初现

以往&#xff0c;奶粉、纸尿裤这类产品基本就代表了整体母婴市场中的消费品。而如今&#xff0c;随着母婴行业的高速发展和消费升级&#xff0c;母婴商品的种类日益丰富&#xff0c;需求也不断深入。 在京东平台&#xff0c;母婴大品类中除了包含婴童相关的食品&#xff08;奶粉…

2022爱分析·事务型关系数据库市场厂商评估报告:万里数据库

目录 1. 研究范围定义 2. 事务型关系数据库市场定义 3. 厂商评估&#xff1a;万里数据库 4. 入选证书 1. 研究范围定义 在国内数字化转型以及信创建设持续推进的大背景下&#xff0c;众多厂商入局国内数据库市场&#xff0c;为企业提供了面向多种应用场景的数据库&am…

「7」线性代数(期末复习)

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 第五章 相似矩阵及二次型 &4&#xff09;对称阵的对角化 &5二次型及其标准型 …

《mysql技术内幕:innodb存储引擎》笔记

任何时候Why都比What重要&#xff1b;不要相信任何的“神话”,学会自己思考&#xff1b;不要墨守成规,大部分人都知道的事情可能是错误的&#xff1b;不要相信网上的传言,去测试,根据自己的实践做出决定&#xff1b;花时间充分地思考,敢于提出质疑。1.MYSQL被设计为一个单进程多…

Elasticsearch也能“分库分表“,rollover实现自动分索引

一、自动创建新索引的方法 MySQL的分库分表大家是非常熟悉的&#xff0c;在Elasticserach中有存在类似的场景需求。为了不让单个索引太过于庞大&#xff0c;从而引发性能变差等问题&#xff0c;我们常常有根据索引大小、时间等创建新索引的需求&#xff0c;解决方案一般有两个…

虚拟 DOM 详解

什么是虚拟 dom&#xff1f; 虚拟 dom 本质上就是一个普通的 JS 对象&#xff0c;用于描述视图的界面结构 在vue中&#xff0c;每个组件都有一个render函数&#xff0c;每个render函数都会返回一个虚拟 dom 树&#xff0c;这也就意味着每个组件都对应一棵虚拟 DOM 树 查看虚拟…

C#中多态、抽象类、虚方法

多态、重装、重写 •多态&#xff1a;同一操作作用于不同的对象&#xff0c;可以有不同的解释&#xff0c;产生不同的执行结果&#xff0c;这就是多态性。抽象类、虚函数、接口三种方法实现的可以是多态性。•重载&#xff08;overload&#xff09;&#xff1a;对象中同名函数&…

JSP 质量管理系统myeclipse定制开发sqlserver数据库网页模式java编程jdbc

一、源码特点 JSP 质量管理系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为SQLServer2008&#xff0c…

二、最基本的vuex的使用

二、最基本的vuex的使用&#xff1a; 学习任何技术&#xff0c;先找到没有用这个技术时&#xff0c;给我们带来了什么麻烦 而这个新技术是怎么帮我们解决这些问题的。 理解方式&#xff1a; state&#xff1a;装数据的一个对象 mutations&#xff1a;装方法的一个对象&#…

hydra常见端口服务穷举

目录 工具介绍 参数说明 官方示例 官方字典 ssh爆破 ftp爆破 mysql爆破 smb爆破 rdb爆破 http爆破 redis爆破 工具介绍 hydra 是一个支持众多协议的爆破工具&#xff0c;已经集成到KaliLinux中&#xff0c;直接在终端打开即可 参数说明 -l &#xff1a; 指定破…

mybatis狂神(附自学过程中疑问解决)

首先先附上mybatis的官方文本链接mybatis – MyBatis 3 | 简介一、Mybatis介绍MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来…

RK3288 GPIO记录

1、引脚对应的GPIO 编号第一种 使用/sys/kernel/debug/gpio查询所有gpio引脚的基数第二种 cat /sys/class/gpio/gpiochip248/label对应的label就是GPIO引脚&#xff0c;例如下图GPIO8对应的基数就是2482、计算编号编号 基数 PIN脚如GPIO8的基数是248&#xff0c; GPIO8_A6的编…

django项目实战三(django+bootstrap实现增删改查)进阶分页

目录 一、分页 1、修改case_list.html页面 2、修改views.py的case_list方法&#xff08;分页未封装&#xff09; 二、分页封装 1、新建类Pagination 2、修改views.py的case_list方法 三、再优化&#xff0c;实现搜索分页qing情况 四、优化其他查询页面实现分页和查询 五…

MySQL —— 内外连接

目录 表的内外连接 一、内连接 二、外连接 1. 左外连接 2. 右外连接 表的内外连接 表的连接分为内连和外连 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们前面博客中的查询都是内连接&#xff0c;也是在开发过程中使用的最多…

Java面试题--熔断和降级的区别

熔断和降级都是系统自我保护的一种机制&#xff0c;但二者又有所不同&#xff0c;它们的区别主要体现在以下几点&#xff1a; 概念不同 触发条件不同 归属关系不同 1.概念不同 1.1熔断概念 “熔断”一词早期来自股票市场。熔断&#xff08;Circuit Breaker&#xff09;也…

JavaWab开发的总括以及HTML知识

一、Web开发的总括在这里我来给大家介绍一下Wab开发需要配合哪些前后端的对应语言:首先是Java(Java通常的工作):Wab开发android开发大数据开发另外,Wab开发想要学好就需要配合之前博客中的内容,如:多线程/IO/网络/数据结构/数据库......这里建议学懂前面的内容再往下走.JavaWab…

Python采集双色球数据,做数据分析,让我自己实现自己的富豪梦

来唠点嗑&#xff1f; 咳咳&#xff0c;最近是咋的了&#xff0c;某站掀起了一股双色球热潮&#xff1f;一般我自己的账号上&#xff0c;是很少看到关于python这些内容的&#xff0c;都是小姐姐和热梗&#xff0c;或者其他搞笑视频 由于&#x1f4b4;的吸引力…手不自觉的就点…

2023年TS4 入门笔记【慕课网imooc】【Vue3+React18 + TS4考勤系统】

目录 安装ts 基础 类型声明和变量声明 类型注解和类型判断 类型分类与联合类型与交叉类型​编辑 never类型与any类型与unknown类型 类型断言与非空断言 数组类型和元祖类型 对象类型与索引签名 函数类型与void类型 函数重载与可调用注解 枚举类型与const枚举 进阶…

2023年美国大学生数学建模A题:受干旱影响的植物群落建模详解+模型代码(二)

前言 资源放CSDN上面过不了审核,都快结束了都没过审真的麻了,订阅专栏的同学直接加我微信直接发你。我只打造优质专栏。专注建模四年,博主参与过大大小小数十来次数学建模,理解各类模型原理以及每种模型的建模流程和各类题目分析方法。此专栏的目的就是为了让零基础快速使…