美文网首页
dubbo怎么解析xml的

dubbo怎么解析xml的

作者: 7d972d5e05e8 | 来源:发表于2020-08-26 18:20 被阅读0次

    一、执行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、向注册中心订阅变化
    当然每个步骤都是细节满满,巨复杂。但是提供者的主流程就是上面。

    相关文章

      网友评论

          本文标题:dubbo怎么解析xml的

          本文链接:https://www.haomeiwen.com/subject/odkfsktx.html