dubbo怎么解析xml的

一、执行org.springframework.beans.factory.xml.PluggableSchemaResolver#getSchemaMappings获取所有的namespace 对应的schemas,即命令空间的解析模板xsd文件

这个方法会被spring自动触发,然后执行

PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);

schemaMappingsLocation=META-INF/spring.schemas,这个就是获取classLoader路径下面所有包的META-INF/spring.schemas文件,然后对spring.schemas文件里面的Key=value,进行key合并。

恰好在dubbo的jar包里面,有个spring.schemas

二、执行org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings获取对应的hanlders

该方法会作用在SpringBoot的ImportResource注解,如下

@ImportResource(locations={"classpath:dubbo/*.xml"})

被org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromImportedResources方法触发loadBeanDefinitions。加载beanDefinition的时候,由于importResource传入的是dubbo的xml。那么又会触发解析xml的过程。如下:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

每个xml的标签都会被解析为Element,spring在转换xml的标签到beanDefinition的过程,使用如下代码:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

是不是很明显了,dubbo的标签语言为啥会被正确解析呢?就是因为使用了DubboNamespaceHandler,这个handler就是通过Element上的命名空间来路由的。

三、DubboNamespaceHandler的解析操作

handler.parse方法会调用,如下:

org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement

这个方法会根据Element,去parsers里面找到一个具体的BeanDefinitionParser,它是一个接口。每个框架都可以自定义规范,只要框架实现了BeanDefinitionParser,那么该框架就可以把自定义格式的文件,转换为BeanDefinition,委托给Spring。
咱们看下DubboNamespaceHandler的parsers的map都有啥,如下:


image.png

看到没,key就是Dubbo框架自定义的语法,通过dubbo实现的DubboBeanDefinitionParser就可以把<service ref="xxx",interface="xxx" group="xxx"/> 这个格式的字符串,转换为beanDefiniton。不就类似spring bean的xml解析嘛。我自己也能任意定义,只要把它与beanDefinition的字段,对应好就没问题。

废话不多说,开始看最重要的部分,解析。

四、DubboBeanDefinitionParser的解析

先看下DubboNamespaceHandler的初始化操作,如下:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
//        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }
}

每个dubbo标签,传入DubboBeanDefinitionParser的class都不一样。说明具体解析操作,肯定会根据class类型不同,获取标签不同的字段。

具体解析如下:

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        //省略
         ....
 if (ProtocolConfig.class.equals(beanClass)) {
            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
                if (property != null) {
                    Object value = property.getValue();
                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                    }
                }
            }
        } else if (ServiceBean.class.equals(beanClass)) {
            String className = element.getAttribute("class");
            if (className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(ReflectUtils.forName(className));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        } else if (ProviderConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
        } else if (ConsumerConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
        }

    //省略
        ....

    }

我们最关心的不就是ServiceBean和ReferenceBean(ConsumerConfig)。以ServiceBean为例,生成的BeanDefinition。

总结:
如果Spring的某一个bean被dubbo进行修饰了,那么spring容器里面会生成两个Bean。一个是spring的普通bean,一个是dubbo的serviceBean类型的bean。这个bean的初始化很简单,主要逻辑在serviceBean的export里面。

export的主要作用就是:
1、构造Invoker,其实就是把spring真正的bean对象进行代理。
2、暴露本地tcp端口,用于通信
3、保存<service,Invoker>到本地,便于上面通信模块接收服务的时候,找到正确的处理Invoker。
4、注册提供者到注册中心
5、向注册中心订阅变化
当然每个步骤都是细节满满,巨复杂。但是提供者的主流程就是上面。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。