Dubbo3 源码系列 Dubbo“纠葛”(入门篇)
日期 更新说明
2022年5月28日 spring xml部分解读
2022年6月3日 spring annotation部分解读
人生不相见, 动如参与商。
今夕复何夕, 共此灯烛光。
少壮能几时, 鬓发各已苍。
访旧半为鬼, 惊呼热中肠。
焉知二十载, 重上君子堂。
昔别君未婚, 儿女忽成行。
怡然敬父执, 问我来何方。
问答未及已, 儿女罗酒浆。
夜雨剪春韭, 新炊间黄粱。
主称会面难, 一举累十觞。
十觞亦不醉, 感子故意长。
明日隔山岳, 世事两茫茫。
杜甫 《赠卫八处士》摘选(文章加粗部分;此情此景觉得有点共鸣哈)分享 一下。
本来计划Dubbo3的源码解读系列,打算两周更新一篇计划;很抱歉第三篇来晚了;可能标题写的有点过于情绪化;勿喷“标题党”,相信我绝对是笔者内容决定认真原创。
前言
上篇文章《Dubbo3 源码系列 -- 环境准备》启示已经介绍了,Dubbo API的方式使用了,可能对于多数使用者,使用Spring集成Dubbo的 xml 和 注解形式可能更多;那么这篇文章从Spring如何集成Spring的支持角度开始解读。
开始之前,源码基于目前Dubbo3.0.7版本;解读可能是目前文档源码有些出入,请注意版本。
Dubbo和Spring部分
Dubbo支持Spring的xml方式
基本使用和案例
其他书籍或者教程;通常会为你编写一个,HelloWorld的案例;笔者觉得,看这篇文章的读者的水平应该是达到了这个级别的;不在手写Dubbo的入门案例;Dubbo官方源码 dubbo-demo提供好了源码案例。
服务提供者编写:
服务消费者编写:
服务提供者、消费者主类
(篇幅问题;提供链接)
provider/Application
consumer/Application
总结:
通知配置Dubbo标签,Spring容器启动时,加载Dubbo相关的Bean,同时生成对应的接口代理,为RPC调用做好准备。
思考:(源码阅读需要解决问题)Dubbo如何支持Spring的?
Dubbo通过代理Bean,实现Spring注入和RPC调用,那么注入哪些Bean,具体细节如何呢?
有了问题,让我们带着问题点阅读源码可能效果更好。
源码解读:
Dubbo在利用Spring可扩展的XML Schema机制时,在Spring启动时加载Dubbo自定义的标签内容(Dubbo的ximl文档[官方文档]);Dubbo的标签较多,这里依照 dubbo:service 配置(dubbo-service文档)为例;可以大体熟悉一下基本用法。
dubbo:service
dubbo:service 配置
服务提供者暴露服务配置。对应的配置类:org.apache.dubbo.config.ServiceConfig
属性 对应URL参数 类型 是否必填 缺省值 作用 描述 兼容性
interface class 必填 服务发现 服务接口名 1.0.0以上版本
ref object 必填 服务发现 服务对象实现引用
Spring可扩展的XML Schema机制
创建一个 XML Schema 文件,描述自定义的合法构建模块,也就是xsd文件
dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
自定义个处理器类,并实现NamespaceHandler接口(比较容易)
- 自定义接口handler实现;懂点实现 parse接口:
● init(): NamespaceHandler被使用之前调用,完成NamespaceHandler的初始化
● BeanDefinition parse(Element, ParserContext): 当遇到顶层元素时被调用
● BeanDefinition decorate(Node,BeanDefinitionHandler,ParserContext): 当遇到一个属性或者嵌套元素的时候调用
@Override
public void init() {
// spring在解析Dubbo.xml标签时,调用模板init方法初始化用于解析Dubbo自定义的XML标签注解的解析器(DubboBeanDefinitionParser)
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class));
registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
/**
* Override {@link NamespaceHandlerSupport#parse(Element, ParserContext)} method
*
* @param element {@link Element}
* @param parserContext {@link ParserContext}
* @return
* @since 2.7.5
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
registerAnnotationConfigProcessors(registry);
// initialize dubbo beans
DubboSpringInitializer.initialize(parserContext.getRegistry());
/**
* 重点调用Spring的org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse方法;
* 就是对应init内部方法的DubboBeanDefinitionParser{@link DubboBeanDefinitionParser#parse(Element, ParserContext)}#parse方法
*/
BeanDefinition beanDefinition = super.parse(element, parserContext);
setSource(beanDefinition);
return beanDefinition;
}
2. 自定义BeanDefinitionParser实现NamespaceHandlerSupport接口;主要解析对应的BeanDefination
org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser;有兴趣自行分析
3. 注册handler和schema
为了让Spring在解析xml的时候能够感知到我们的自定义元素,我们需要把namespaceHandler和xsd文件放到2个指定的配置文件中,这2个文件都位于META-INF目录中
Spring通过XML解析程序将其解析为DOM树,通过NamespaceHandler指定对应的Namespace的BeanDefinitionParser将其转换成BeanDefinition。再通过Spring自身的功能对BeanDefinition实例化对象。
在期间,Spring还会加载两项资料:
○ META-INF/spring.handlers
指定NamespaceHandler(实现org.springframework.beans.factory.xml.NamespaceHandler)接口,或使用org.springframework.beans.factory.xml.NamespaceHandlerSupport的子类。
○ META-INF/spring.schemas
在解析XML文件时将XSD重定向到本地文件,避免在解析XML文件时需要上网下载XSD文件。通过现实org.xml.sax.EntityResolver接口来实现该功能
4. 对于注册Context容器;这里是Spring自定义机制;目前由于篇幅问题;多做过多的细节介绍:(请看图)
Spring Bean 加载流程分析(通过 XML 方式加载)
Spring-ClassPathXmlApplicationContext源码探讨-解析XML文件_半笙彷徨的博客-CSDN博客
Spring 的annotation部分源码
基本使用和案例
服务接口
服务提供者:
源码解读
使用注解需要重点关注:@PropertySource、@DubboService、@DubboReference 就可以了
思考:
- Dubbo的注解都干了些啥?(这个基本看下注释和源码基本都可以明白)
- Dubbo如何如何让Spring识别Dubbo自定义的注解呢?
- 还记得之前注解对应的properties文件吗?这个也作为小问题点
源码入口 -- @EnableDubbo
● @EnableDubboConfig :加载Dubbo相关配置
需要配置多个对象(ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等),而这个注解的目的就是帮我们简化这些配置,只要将相应的配置项通过.properties文件交由@PropertySource注解由Spring加载到Environment中,Dubbo即可通过Environment中相应的属性与ApplicationConfig对象同名的属性进行绑定,而这个过程都是自动。
● @DubboComponentScan:扫描类路径以获取将自动注册为Spring bean的注释组件。
@EnableDubboConfig
@Import(DubboConfigConfigurationRegistrar.class)注解导入;用于解析和加载通用的Bean :
// ImportBeanDefinitionRegistrar 注解 配合 @Import使用
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// initialize dubbo beans(Spring从初始化,加载Dubbo相关的Bean)
DubboSpringInitializer.initialize(registry);
// Config beans creating from props have move to ConfigManager
// AnnotationAttributes attributes = AnnotationAttributes.fromMap(
// importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
//
// boolean multiple = attributes.getBoolean("multiple");
//
// // Single Config Bindings
// registerBeans(registry, DubboConfigConfiguration.Single.class);
//
// if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
// registerBeans(registry, DubboConfigConfiguration.Multiple.class);
// }
}
}
DubboBeanUtils#registerCommonBeans
/**
* Register the common beans
*
* @param registry {@link BeanDefinitionRegistry}
* @see ReferenceAnnotationBeanPostProcessor
* @see DubboConfigDefaultPropertyValueBeanPostProcessor
* @see DubboConfigAliasPostProcessor
* @see DubboBootstrapApplicationListener
*/
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);
// Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
// registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
// DubboBootstrapApplicationListener.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);
}
总结:
使用 @EnableDubbo加载Dubbo对应的共有配置Bean和postprocesser,扫描注解,加载@Service和@reference注解。更多内容启示关于Spring的框架的底层内容熟悉,Dubbo只是灵活的使用Spring的API。
帮助文档
xsd-custom-registration
Spring中的XML schema扩展机制
Spring容器启动流程+Bean的生命周期【附源码】 - 天乔巴夏丶 - 博客园 (cnblogs.com)
缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制 - 掘金 (juejin.cn)
Dubbo3 源码系列 Dubbo“纠葛”(入门篇)的更多相关文章
- Dubbo3 源码系列 -- 环境准备
Dubbo3 源码系列 -- 环境准备 前言 工作中一直使用Dubbo项目,借着这次机会通过源码的方式来学习下Dubbo的源码内容.目前市面上很多都是的Dubbo2系列的教程:就连目前的Dubbo的官 ...
- 手牵手,从零学习Vue源码 系列一(前言-目录篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...
- 手牵手,从零学习Vue源码 系列二(变化侦测篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...
- SpringCloud 源码系列(6)—— 声明式服务调用 Feign
SpringCloud 源码系列(1)-- 注册中心 Eureka(上) SpringCloud 源码系列(2)-- 注册中心 Eureka(中) SpringCloud 源码系列(3)-- 注册中心 ...
- Ioc容器BeanPostProcessor-Spring 源码系列(3)
Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...
- AOP执行增强-Spring 源码系列(5)
AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...
- Android -- 带你从源码角度领悟Dagger2入门到放弃
1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...
- Android -- 带你从源码角度领悟Dagger2入门到放弃(二)
1,接着我们上一篇继续介绍,在上一篇我们介绍了简单的@Inject和@Component的结合使用,现在我们继续以老师和学生的例子,我们知道学生上课的时候都会有书籍来辅助听课,先来看看我们之前的Stu ...
- 大白话Vue源码系列(02):编译器初探
阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...
随机推荐
- ABP源码分析 - 约定注册(2)
比较随意,记录下过程,以便忘了以后重拾. 所谓约定注册是指不需要明确写代码注入,只需要按约定规则写服务类,框架自动完成服务注册. 例如,只要这样写,框架就会自动注册. public class Tax ...
- Mybatis-Dao层实现(通过代理方式)
1.代理方式开发是主流 2.Mapper接口开发方法只需要编写Mapper接口(相当于Dao接口),然后由Mybatis根据接口创建动态代理对象 Mapper接口开发需要遵循以下规范 一一对应 Use ...
- 在原有mysql机器上增加一台实例
采用的是yum install mysql-community-server yum方式安装mysql(社区版) 文章基础上新加一个mysql实例. 这个完全可以直接实战上应用,只要规划好即可 服务器 ...
- 认识 vh 和 vw 单位
1. 页面 html 结构 <header> <h1>欢迎来到米修在线</h1> <p>Lorem ipsum dolor sit amet conse ...
- Em 和 Rem 的基本使用
1. Em html 结构 <div id="box-1"> <h3>Box One</h3> <p> Lorem ipsum do ...
- nginx wordpress安装
BEGIN; 最近在弄wordpress,然后环境是centOS6.3,使用nginx做web服务器. 经过一系列安装,nginx.mysql.php都安装成功了,然后在配置nginx访问wordpr ...
- 『现学现忘』Git基础 — 13、Git的基础操作
目录 1.Git最基础的使用方式 (1)初始化本地版本库 (2)查看文件的状态 (3)把文件添加到暂存区 (4)把暂存区的内容提交到本地版本库 2.总结本文用到的Git命令 1.Git最基础的使用方式 ...
- Linux ubuntu下docker容器安装和基础命令
Docker介绍: 云计算就好比大货轮,docker就是集装箱虚拟机虽然可以隔离出很多"子电脑",但占用空间更大,启动更慢,虚拟机软件可能还要花钱(例如VMWare). 而容器技术 ...
- 【在下版本,有何贵干?】Dockerfile中 RUN yum -y install vim失败Cannot prepare internal mirrorlist: No URLs in mirrorlist
隐秘的版本问题---- Dockerfile中 RUN yum -y install vim失败Cannot prepare internal mirrorlist: No URLs in mirro ...
- 利用QEMU+GDB搭建Linux内核调试环境
前言 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试. 其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qe ...