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; } }