日期 更新说明

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提供好了源码案例。

  1. 服务提供者编写:

  2. 服务消费者编写:

  3. 服务提供者、消费者主类

    (篇幅问题;提供链接)

    provider/Application

    consumer/Application

    总结:

    通知配置Dubbo标签,Spring容器启动时,加载Dubbo相关的Bean,同时生成对应的接口代理,为RPC调用做好准备。

    思考:(源码阅读需要解决问题)

  4. Dubbo如何支持Spring的?

  5. 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接口(比较容易)

  1. 自定义接口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 就可以了

思考:

  1. Dubbo的注解都干了些啥?(这个基本看下注释和源码基本都可以明白)
  2. Dubbo如何如何让Spring识别Dubbo自定义的注解呢?
  3. 还记得之前注解对应的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“纠葛”(入门篇)的更多相关文章

  1. Dubbo3 源码系列 -- 环境准备

    Dubbo3 源码系列 -- 环境准备 前言 工作中一直使用Dubbo项目,借着这次机会通过源码的方式来学习下Dubbo的源码内容.目前市面上很多都是的Dubbo2系列的教程:就连目前的Dubbo的官 ...

  2. 手牵手,从零学习Vue源码 系列一(前言-目录篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...

  3. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

  4. SpringCloud 源码系列(6)—— 声明式服务调用 Feign

    SpringCloud 源码系列(1)-- 注册中心 Eureka(上) SpringCloud 源码系列(2)-- 注册中心 Eureka(中) SpringCloud 源码系列(3)-- 注册中心 ...

  5. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  6. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  7. Android -- 带你从源码角度领悟Dagger2入门到放弃

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  8. Android -- 带你从源码角度领悟Dagger2入门到放弃(二)

    1,接着我们上一篇继续介绍,在上一篇我们介绍了简单的@Inject和@Component的结合使用,现在我们继续以老师和学生的例子,我们知道学生上课的时候都会有书籍来辅助听课,先来看看我们之前的Stu ...

  9. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

随机推荐

  1. git clone 遇到的问题

    经常遇到这个问题, 所以今天决定把它记录下来 当使用git clone时,会报Please make sure you have the correct access rights and the r ...

  2. Typora 设置图片自动上传

    使用 PicGo-Core(command line) 下载 PicGo-Core 依次点击 文件 -> 偏好设置 -> 图像 来到下图所示界面: 点击①位置选择 PicGo-Gore(c ...

  3. redis集群在线迁移第一篇(数据在线迁移至新集群)实战一

    迁移背景:1.原来redis集群在A机房,需要把其迁移到新机房B上来.2.保证现有环境稳定.3.采用在线迁移方式,因为原有redis集群内有大量数据.4.如果是一个全新的redis集群搭建会简单很多. ...

  4. Java-GUI编程之绘图

    绘图 很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时,有时候也必须"动态"地向客户 端生成各种图形.图表,比如 图形验证码.统计图等,这都需 ...

  5. Postman中文版客户端

    "新冷战"蔓延到生产力工具 前端时间,著名 UI 设计软件 Figma 宣布制裁大疆! 这不仅让中国的互联网从业者纷纷一头冷汗:今天是 Figma,明天会是什么?今天是大疆,明天会 ...

  6. Mybatis项目无法初始化异常

    该异常是Maven资源导出时出错,.xml文件或者.properties文件不能正常导出所致,最简单的办法就是在目标文件上复制粘贴一份.xml文件或者是.properties文件: 但是实际应用的过程 ...

  7. Dockerfile 命令详解及最佳实践

    Dockerfile 命令详解 FROM 指定基础镜像(必选) 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制.就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指 ...

  8. Spring 源码 (2)Spring IOC 容器 前戏准备工作

    Spring 最重要的方法refresh方法 根据上一篇文章 https://www.cnblogs.com/redwinter/p/16141285.html Spring Bean IOC 的创建 ...

  9. 公私钥 SSH 数字证书

    公私钥 SSH 数字证书 小菜鸟今天买了华为云一台服务器,在使用公私钥远程登录服务器的时候,忘记了相关公钥私钥的原理和一些应用了,今天复习一波做个记录. 相关概念 公钥:公钥用来给数据加密,用公钥加密 ...

  10. 转换为布尔类型 Boolean

    1. js 代码 console.log(Boolean('')); // false console.log(Boolean(0)); // false console.log(Boolean(Na ...