百木园-与人分享,
就是让自己快乐。

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

  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)


来源:https://www.cnblogs.com/weir110/p/16340190.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » Dubbo3 源码系列 Dubbo“纠葛”(入门篇)

相关推荐

  • 暂无文章