dubbo源码阅读: dubbo的xml文件如何解析的?

news2025/4/27 17:35:44

dubbo源码阅读: dubbo的xml文件如何解析的?

  • DubboNamespaceHandler
  • spring 的接口 NamespaceHandler
  • spring 的抽象类 NamespaceHandlerSupport
  • 学以致用

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="demo-provider" >
    </dubbo:application>

    <dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
    <dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181?registry-type=service"/>

    <dubbo:protocol name="dubbo" port="-1"/>
    <dubbo:protocol name="rest" port="-1"/>
    <dubbo:protocol name="tri" port="-1"/>

    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
    <bean id="greetingService" class="org.apache.dubbo.demo.provider.GreetingServiceImpl"/>
    <bean id="restDemoService" class="org.apache.dubbo.demo.provider.RestDemoServiceImpl"/>
    <bean id="tripleService" class="org.apache.dubbo.demo.provider.TripleServiceImpl"/>

    <dubbo:service delay="5000" interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService" registry="registry1" protocol="dubbo"/>
    <dubbo:service delay="5000" version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.demo.GreetingService"
                   ref="greetingService" protocol="dubbo"/>
    <dubbo:service delay="5000" version="1.0.0" timeout="5000" interface="org.apache.dubbo.demo.RestDemoService"
                   ref="restDemoService" protocol="rest"/>
    <dubbo:service delay="5000" version="1.0.0" timeout="5000" interface="org.apache.dubbo.demo.TripleService"
                   ref="tripleService" protocol="tri"/>

</beans>

我们在使用dubbo的时候,更多的是用spring的xml配置方式,我们今天了解下这些配置文件是如何解析的

DubboNamespaceHandler

在这里插入图片描述

@Override
    public void init() {
        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("tracing", new DubboBeanDefinitionParser(TracingConfig.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());
    }

从这个init方法中我们可以看到 spring容器初始化的时候会调用这个init方法,dubbo在DubboNamespaceHandler的init中添加了每一个标签对应的一个解析类

spring 的接口 NamespaceHandler

public interface NamespaceHandler {
	/**
	 *	初始化向spring容器中注册bean定义解析器
	 */
    void init();
	/**
	 *	解析函数
	 */
    BeanDefinition parse(Element var1, ParserContext var2);
	// 针对bean 进行装饰
    BeanDefinitionHolder decorate(Node var1, BeanDefinitionHolder var2, ParserContext var3);
}

spring 的抽象类 NamespaceHandlerSupport

NamespaceHandlerSupport则是NamespaceHandler的实现,但仍然是抽象类

private final Map<String, BeanDefinitionParser> parsers = new HashMap();
    private final Map<String, BeanDefinitionDecorator> decorators = new HashMap();
    private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap();

这三个HashMap中装的分别是:Bean定义解析器和装饰器

    private final Map<String, BeanDefinitionParser> parsers = new HashMap();
    private final Map<String, BeanDefinitionDecorator> decorators = new HashMap();
    private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap();

    public NamespaceHandlerSupport() {
    }

    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
    	//获取解析器
        BeanDefinitionParser parser = this.findParserForElement(element, parserContext);
        //调用解析器的parse 方法
        return parser != null ? parser.parse(element, parserContext) : null;
    }

	// 根据Element 中的标签获取 localName 这都是提前约定好的,再从map 中获取解析器
    @Nullable
    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        String localName = parserContext.getDelegate().getLocalName(element);
        BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }

        return parser;
    }

		//实现父类decorate方法和上述类似,只不过是从decorators 和 attributeDecorators中查找:
    @Nullable
    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
        BeanDefinitionDecorator decorator = this.findDecoratorForNode(node, parserContext);
        return decorator != null ? decorator.decorate(node, definition, parserContext) : null;
    }

    @Nullable
    private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
        BeanDefinitionDecorator decorator = null;
        String localName = parserContext.getDelegate().getLocalName(node);
        if (node instanceof Element) {
            decorator = (BeanDefinitionDecorator)this.decorators.get(localName);
        } else if (node instanceof Attr) {
            decorator = (BeanDefinitionDecorator)this.attributeDecorators.get(localName);
        } else {
            parserContext.getReaderContext().fatal("Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
        }

        if (decorator == null) {
            parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " + (node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
        }

        return decorator;
    }
//最后是三个注册方法,分别向spring容器中注册parser,decorator,本质上是放入HashMap中:
    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
        this.parsers.put(elementName, parser);
    }

    protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
        this.decorators.put(elementName, dec);
    }

    protected final void registerBeanDefinitionDecoratorForAttribute(String attrName, BeanDefinitionDecorator dec) {
        this.attributeDecorators.put(attrName, dec);
    }

最后完成了RootBeanDefinition ,也就是bean 定义

学以致用

加入我们有这么一个场景,我们在消费mq 消息时,消息体中有一个消息类型我们根据不同的类型进行不同的消息处理,最简单的就是使用if 。。else 或者使用switch

public class AliyunMQListener implements MessageListener {  
    private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);  
  
    @Resource  
    private MQConsumerService mqConsumerService;  
  
    @Override  
    public Action consume(Message message, ConsumeContext consumeContext) {  
        try {  
            String data = new String(message.getBody(), "UTF-8");  
            logger.info("接收到消息,{}", data);  
            if (message.getTopic().equals(SystemParamInit.getMQTopic())) {  
                switch (message.getTag()) {  
                    case CommonValue.TOPIC_TAG:  
                        mqConsumerService.checkSensitiveWord4Topic(JsonUtils.toBean(data, TopicContent.class));  
                        break;  
                    case CommonValue.COMMENT_TAG:  
                        mqConsumerService.checkSensitiveWord4Comment(JsonUtils.toBean(data, CommentContent.class));  
                        break;  
                    case CommonValue.TOPIC_CREATOR_BANNED_TAG:  
                        mqConsumerService.updateUserTopic(JsonUtils.toBean(data, TopicCreatorBanned.class));  
                        break;  
                }  
            }  
        } catch (Exception e) {  
            logger.error("消息消费失败,{}", e);  
            return Action.ReconsumeLater;  
        }  
        return Action.CommitMessage;  
    }  
}  

这样写的代码就是 随着消息不类型不断增多,代码不易维护,也不符合开闭原则

Spring的自定义标签解析是通过,写一个继承自NamespaceHandlerSupport的类,并实现init()方法,在init()方法中,去注册解析器。然后在解析xml时,通过约定的key去Map中拿到相应的解析器进行解析。大致思路有了,就开始对上面的逻辑进行改造。对应的设计模式为:接口-适配器模式、抽象工厂模式、策略模式及模板方法模式

首先,我们需要定义一个消息解析器接口,解析器的实现就是对相应tag的消息的处理

public interface IMessageParser<T> {  
    JmsAction parse(T message);  
}  

然后,定义一个消息处理器接口,包含初始化方法、获取路径及接受Message实现消息分发的逻辑

public interface IMessageHandler<T> {  
    void init();  
  
    String getDestination(T message);  
  
    JmsAction parse(T message);  
} 

接着,定义消息处理器的抽象类。形如NamespaceHandlerSupport。这边要实现消息的分发和解析器的注册

public abstract class MessageHandlerSupport<T> implements IMessageHandler<T> {  
  
    private final Map<String, IMessageParser> parsers = new ConcurrentHashMap<>();  
  
    @Override  
    public JmsAction parse(T message) {  
        return findParserForMessage(message).parse(message);  
    }  
  
    private IMessageParser findParserForMessage(T message) {  
        IMessageParser parser = this.parsers.get(getDestination(message));  
        if (parser == null) {  
            throw new MessageParserException("No MessageParser is matched,Destination is " + getDestination(message));  
        }  
        return parser;  
    }  
  
    protected final void registerMessageParser(String elementName, IMessageParser parser) {  
        this.parsers.put(elementName, parser);  
    }  
  
}  

之后,定义一个处理器容器注册类。在Spring容器启动的时候,需要进行初始化,将需要的消息处理器放入其中,顺带提供个选择处理器的方法

public class MessageHandlerRegister {  
    private Map<String, IMessageHandler> container = new ConcurrentHashMap<>();  
  
    public Map<String, IMessageHandler> getContainer() {  
        return container;  
    }  
  
    public void setContainer(Map<String, IMessageHandler> container) {  
        this.container = container;  
    }  
  
    public IMessageHandler findMessageHandler(String topicName) {  
        if (CollectionUtils.isEmpty(container)) {  
            return null;  
        } else {  
            return container.get(topicName);  
        }  
    }  
}  

最后,再对原来的Listener进行调整

public class AliyunMQListener implements MessageListener {  
  
    private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);  
  
    @Resource  
    private MessageHandlerRegister messageHandlerRegister;  
  
    @Override  
    public Action consume(Message message, ConsumeContext context) {  
        IMessageHandler messageHandler = messageHandlerRegister.findMessageHandler(message.getTopic());  
        if (null == messageHandler) {  
            logger.warn("No MessageHandler is matched,topic is {}", message.getTopic());  
        } else {  
            messageHandler.parse(message);  
        }  
        return Action.CommitMessage;  
    }  
  
}  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/578756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

征文 | 吸引铁粉?成为CSDN明星!

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 征文 | 吸引铁粉&#xff1f;成为CSDN明星&#xff01; 导读 当今数字时代&#xff0c;社交媒体和在线社区成为了人们交流和分享的主要平台之一&#xff0c;CSDN就是其…

前沿重器[34] | Prompt设计——LLMs落地的版本答案

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

31 Vue 表单输入绑定的实现

前言 这是最近的碰到的那个 和响应式相关的问题 特定的操作之后响应式对象不“响应“了 引起的一系列的文章 主要记录的是 vue 的相关实现机制 呵呵 理解本文需要 vue 的使用基础, js 的使用基础 测试用例 测试用例如下, 一个简单的 v-model 的使用 问题的调试 这里 …

尝试在UNet的不同位置添加SE模块

目录 &#xff08;1&#xff09;se-unet01&#xff08;在卷积后&#xff0c;下采样前&#xff0c;添加SE模块&#xff09; &#xff08;2&#xff09;se-unet02&#xff08;在卷积后&#xff0c;上采样前&#xff0c;添加SE模块&#xff09; &#xff08;3&#xff09;se-un…

Qt基础之三十六:异常处理

本文将介绍如何在Qt中使用try...catch和调试dump文件来处理异常。 Qt版本5.12.6 一.使用try...catch 一段简单的捕获异常的代码,新建一个控制台工程,pro文件不用修改 #include <QCoreApplication> #include <QDebug>int main(int argc, char *argv[]) {QCoreA…

历届蓝桥杯青少年编程比赛 计算思维题真题解析【已更新5套 持续更新中】

一、计算思维组考试范围 计算思维组面向小学生&#xff08;7-12 岁&#xff0c;约 1-6 年级&#xff09;&#xff0c;通过设计多个角度的考核题目、层次科学的试卷组合、线上限时的考试形式&#xff0c;更加精确地考查学生的计算能力、反应能力、思维与分析能力&#xff0c;使…

注解实现自动装配

要使用注解须知&#xff1a; 1.导入约束 context约束 2.配置注解的支持 官方配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/…

Flume实践

1 NetCat方式 ]# ./bin/flume-ng agent --conf conf--conf-file ./conf/flume_netcat.conf --name a1 -Dflume.root.loggerINFO,console [rootmaster ~]# yum -y intalll telnet 发数据&#xff1a; ]# telnet master 44444 数据接收&#xff0c;是在终端上接收的&#xff0…

[创业之路-70] :聊天的最高境界因场景不同而不同

一、聊天的最高境界因场景不同而不同。 销售式聊天&#xff1a; 聊天的最高境界不是真相&#xff0c;而是拨动对方的心弦&#xff0c;让对方心理爽&#xff0c;让对方舒心。聊天的结果是你买单。 消费式聊天&#xff1a; 聊天的最高境界不是真相&#xff0c;而是让自己心理爽…

Java面试集锦

1. html与jsp区别&#xff1f; 答&#xff1a;HTML是文本标记语言&#xff0c;它是静态页面&#xff1b;JSP页面是有JSP容器执行该页面的Java代码部分然后实时生成动态页面&#xff0c;可动态更新页面上的内容。 在jsp中用<%%>就可以写Java代码了&#xff0c;而html没有…

数组的存储和压缩

数组 定义 一维数组是有限个相同类型的数据元素构成的序列&#xff0c;逻辑关系是相邻关系 推广&#xff1a;一个二维数组可以看作相同类型的一维数组的一维数组&#xff1b;n维数组可以看作以n-1维数组作为元素的线性表 性质 数组中的数据元素数目…

提前布局

深圳最近的天气是美&#xff0c;有风&#xff0c;天空可以看得见飘来飘去的透明的云&#xff0c;傍晚时候也可以看见城市被霞光笼罩下那种很安静要睡去的样子&#xff0c;随处可以见到的青草绿叶和大树&#xff0c;从公司楼下穿过的一个小桥洞前看得见的红绿路牌也安静和谐。 今…

将 Nacos 转变为 Windows 系统服务,实现开机自启

文章目录 前言下载 WinSW配置 WinSW安装和启动 Nacos 服务联系我 前言 本文将为您介绍如何使用 WinSW 工具将 Nacos 打包成 Windows 系统服务&#xff0c;并实现开机自启动的便利功能。通过将 Nacos 安装为系统服务&#xff0c;您将摆脱每次手动启动的麻烦&#xff0c;从而提高…

论文阅读_增强语言模型综述

论文信息 name_en: Augmented Language Models: a Survey name_ch: 增强语言模型综述 paper_addr: http://arxiv.org/abs/2302.07842 date_read: 2023-05-20 date_publish: 2023-02-15 tags: [‘深度学习’,‘自然语言处理’,‘大模型’] author: Grgoire Mialon&#xff0c;M…

jQuery样式操作和效果操作

1. css方法 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width,…

day8--链表倒数第k个结点

链表中倒数最后k个结点 双指针法 是定义两个指针p和q&#xff0c;先让p指向链表的头结点&#xff0c;然后让q指向第k个结点。 接着&#xff0c;同时移动p和q&#xff0c;直到q指向链表的尾结点。此时&#xff0c;p指向的结点就是倒数第k个结点 struct ListNode {int val;Li…

git commit规范

目录 一、代码提交风格&#xff1a; 二、代码提交验证: 一、代码提交风格&#xff1a; 通常我们的git commit会按照统一的风格来提交&#xff0c;这样可以快速定位每次提交的内容&#xff0c;方便之后对版本进行控制。 但是如果每次手动来编写这些是比较麻烦的事情&#xff0…

【Flutter】widgets (1) 组件概述 widget tree 常见的widgets

文章目录 一、前言二、如何理解 widgets三、widgets 和 Div 布局很像四、常见的组件五、总结一、前言 在 Flutter 中,所有的 UI 元素都被称为 widgets,包括整个应用程序本身。一个 Flutter 应用通常由多个小的 widgets 组合而成,这些 widgets 可以是文本,按钮,图片,甚至…

JetBrains的C和C++集成开发环境CLion 2023版本在Linux系统的下载与安装配置教程

目录 前言一、CLion安装二、使用配置总结 前言 CLion是一款为C和C语言开发人员设计的集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署C和C应用程序。注&#xff1a;已在CentOS7.9和Ubuntu20.04安装测…

《数据库》期末考试复习手写笔记-第10章 数据库恢复技术(日志文件+检查点)【10分】

知识点:事务+日志文件+检查点 考题一:日志记录 考题二:数据库恢复 如果一个数据库恢复系统采用检查点机制,且其日志文件如表4所示