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

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

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