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

news/2024/11/8 4:34:38/

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.xsdhttp://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

在这里插入图片描述

@Overridepublic 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() {}@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {//获取解析器BeanDefinitionParser parser = this.findParserForElement(element, parserContext);//调用解析器的parse 方法return parser != null ? parser.parse(element, parserContext) : null;}// 根据Element 中的标签获取 localName 这都是提前约定好的,再从map 中获取解析器@Nullableprivate 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中查找:@Nullablepublic BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {BeanDefinitionDecorator decorator = this.findDecoratorForNode(node, parserContext);return decorator != null ? decorator.decorate(node, definition, parserContext) : null;}@Nullableprivate 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.ppmy.cn/news/97864.html

相关文章

微信小程序开发工具介绍及安装(上)

本章主要介绍 微信小程序开发工具的介绍小程序开发工具的安装方法开发工具的基本功能介绍 微信小程序开发工具的介绍 微信小程序开发工具是一款由微信官方提供的集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在帮助开发者更便捷地创建、调试和发布微信小程序。该开…

SAM-MM-配额协议字段解析

1、有效起始日&#xff1a;配额协议的生效日期&#xff0c;系统默认为当前创建日期&#xff0c;灰色&#xff0c;不可更改&#xff1b; 2、有效至&#xff1a;配额协议的失效日期&#xff0c;要手动填一个日期&#xff1b; 3、最小数量拆分&#xff1a;如果要按照配额比例&am…

如何在华为OD机试中获得满分?Java实现【获取最大软件版本号】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

Rocketmq学习之路(一)从生产上的问题引出MQ

前言&#xff1a; 从来没有真正弄明白mq是什么&#xff0c;只知道他有消峰&#xff0c;异步&#xff0c;解耦的作用。但是在日常开发工作中&#xff0c;就是简单的生产者发送消息&#xff0c;消费者接受消息。所以&#xff0c;从今天开始。我要吃掉这个技术。 一.这该死的订单…

SAP-MM-采购申请字段解析

采购申请抬头以及行项目字段解析 1、采购申请类型&#xff1a; 对PR进行分类&#xff1b; 控制PR行项目的编号间隔&#xff1b; 控制PR编号范围&#xff0c;以及是否内/外部给号&#xff1b; 控制PR的屏幕选择格式&#xff1b; 控制PR是否允许凭证抬头审批&#xff0c;如果允…

Redis - Redis为什么快

根据官方数据&#xff0c;Redis 的 QPS 可以达到约 100000&#xff08;每秒请求数&#xff09;&#xff0c;有兴趣的可以参考官方的基准程序测试《How fast is Redis&#xff1f;》&#xff0c;官方地址&#xff1a; https://redis.io/topics/benchmarks 横轴是连接数&#xf…

如何通俗地解释云计算、私有云、公有云、混合云、专有云、分布式云?

很多技术人员怎么也讲不清楚的云计算&#xff0c;听完下面的解释&#xff0c;想必心里能明白个七七八八&#xff1a; 你娶了一个老婆&#xff0c;这叫传统IT架构。 你觉得一个老婆不够&#xff0c;这叫传统企业CIO的困境。 你又娶了一个老婆&#xff0c;这叫双活数据中心。 …

使用kotlin用回溯法解决电话号码的字母组合问题

17. 电话号码的字母组合 难度中等 2474 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#…