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