日期 更新说明

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. javaweb图书管理系统之不同用户跳转不同页面

    关于分级自测题,我们知道该系统一共分为两个角色,一个是读者,一个是管理员,我们需要根据不同用户去到不同的页面,所以我们需要写一个登陆界面. 本文先写这个功能的实现,该功能主要在servlet里面实现. ...

  2. php实验一 html网页设计

    页面展示: 源码demo: 等我传到github

  3. JAVA对XML文件的读写

    XML 指可扩展标记语言(EXtensible Markup Language),是独立于软件和硬件的信息传输工具,应用于 web 开发的许多方面,常用于简化数据的存储和共享. xml指令处理指令,简 ...

  4. input禁止输入空格 以及 input各种输入限制

    1.禁止输入空格 方法1:  oninput="value=value.replace(/\s+/g,'')"   在 ios中  用手机原生键盘  输入  会出现bug,  就是 ...

  5. html是什么,html5是什么?web开发必备知识之html

    如果你要写一篇文章,你可以能会这样写:"我是小明,今年6岁了,现在在上小学一年级.我喜欢吃鲍鱼." 当时如果你像让"鲍鱼"这两个字红色并且字体大一点怎么办?? ...

  6. 安卓电池健康查看软件AccuBattery 分享

    一.天下苦秦久矣 说实话,我是小米的忠实粉丝(雷总打钱),手里目前是红米k30pro标准版, 室友中有用华为也有苹果的,据我所知苹果系统是可以看到电池健康的,但是安卓却不行, 所以推荐大家一个安卓软件 ...

  7. Golang | 并发

    goroutine 协程(Coroutine) Golang 在语言层面对并发编程进行了支持,使用了一种协程(goroutine)机制, 协程本质上是一种用户态线程,不需要操作系统来进行抢占式调度,但 ...

  8. 让我们写一个 Win32 文本编辑器吧 - 2. 计划和显示

    让我们写一个 Win32 文本编辑器吧 - 2. 计划和显示 如果你已经阅读了简介,相信你已经对我们接下来要做的事情有所了解. 本文,将会把简介中基础程序修改为一个窗体应用程序.并对编辑器接下来的编辑 ...

  9. formdata收集数据

    通常在收集表单的时候我们都要涉及到绑定上传附件,这时候就可以用formdata的形式携带文件流上传给服务器. formData是ajax2.0(XMLHttpRequest Level2)新提出的接口 ...

  10. Spring-注入方式(基于xml方式)

    1.基于xml方式创建对象 <!--配置User类对象的创建 --> <bean id="user" class="com.at.spring5.Use ...