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 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...
随机推荐
- 横竖屏切换android:screenOrientation属性的使用
在开发android的应用中,有时候需要限制横竖屏切换,只需要在AndroidManifest.xml文件中加入android:screenOrientation属性限制. android:sc ...
- JDK安装和卸载
安装:https://blog.csdn.net/Cassiel_Paris/article/details/98941767 卸载:https://www.cnblogs.com/WZ-BeiHan ...
- SpringMVC基于注解开发的步骤
基于xml配置 .1准备好以下相关jar包 .2创建Maven项目使用骨架 (这里选择第二个以webapp结尾的非第一个) 给项目起个名字 这里可以更改maven本地仓库(依赖包所存放的地方)的路径 ...
- Spring-Bean依赖注入(引用数据类型和集合数据类型)
为什么使用spring依赖注入详见–>依赖注入分析 1.创建实体类User类 package com.hao.domain; public class User { private String ...
- DirectX11 With Windows SDK--38 级联阴影映射(CSM)
前言 在31章我们曾经实现过阴影映射,但是受到阴影贴图精度的限制,只能在场景中相当有限的范围内投射阴影.本章我们将以微软提供的例子和博客作为切入点,学习如何解决阴影中出现的Atrifacts: 边缘闪 ...
- 中文版Postman
作为软件开发从业者,API 调试是必不可少的一项技能,在这方面 Postman 做的非常出色.但是在整个软件开发过程中,API 调试只是其中的一部分,还有很多事情 Postman 无法完成,或者无法高 ...
- Warmup小记
什么是warmup 热身,在刚刚开始训练时以很小的学习率进行训练,使得网络熟悉数据,随着训练的进行学习率慢慢变大,到了一定程度,以设置的初始学习率进行训练,接着过了一些inter后,学习率再慢慢变小: ...
- for .. range中的坑
最近在开发中使用了for range来遍历一个slice,结果在测试的时候遇到了bug,最后定位是错误使用for range造成的,这里记录一下: func redisSlaveScanBigKeys ...
- 背包问题dp的初步总结
背包问题 01背包 给定的物体只有0个和1个,只有选与不选的划分,其状态转移方程时由i-1行推出,所以第二层循环是由j=m,递减到v[i]的. for(int i=1;i<=n;i++){ fo ...
- jsp第一周作业
环境搭建,运行出来一个JSP页面,显式hello 英文字母表 <%@ page language="java" import="java.util.*" ...