• Dubbo解析之初始化过程分析


    上一节,通过与Spring集成的实例,把Dubbo项目跑了起来。但是Dubbo项目是怎么运行起来的呢?它的入口在哪里?在官网上有这么一句话:Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展进行加载。

    一、Spring的初始化

    1、解析配置文件

    • 加载配置文件
    • 封装成Resource对象,然后解析为Document对象
    • 根据Document节点信息,封装Bean对象,并注册
    其中,封装Bean信息,就是调用parseBeanDefinitions处理配置文件中的节点信息。
    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
        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);
            }
        }
    }
     

    2、查找命名空间

    上面的方法里面,大部分时候会走到parseCustomElement(ele); 它通过获取配置文件中的namespaceUri,来获得NamespaceHandler,然后调用其parse方法,完成对Bean的注册。
    public class BeanDefinitionParserDelegate {
    
        public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
            //在Dubbo中,namespaceUri就是http://dubbo.apache.org/schema/dubbo
            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;
            }
            //调用方法,完成对Bean的加载和注册
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    } 
    那么,问题的关键就变成了:如何来一个确定NamespaceHandler?
    在Spring中,它会通过AppClassLoader加载所有的META-INF/spring.handlers文件,转换成Map<string, object=""> handlerMappings,那么在Dubbo源代码中,就有一个这样的文件。内容为:
    http://dubbo.apache.org/schema/dubbo=
    com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    http://code.alibabatech.com/schema/dubbo=
    com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    很显然,namespaceUri为http://dubbo.apache.org/schema/dubbo就对应com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler处理类。然后Spring会通过反射,实例化DubboNamespaceHandler对象,调用其初始化方法init()。
    NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
    namespaceHandler.init();
    handlerMappings.put(namespaceUri, namespaceHandler); 
    初始化之后,返回NamespaceHandler对象,调用parse()来处理Bean。

    3、DubboNamespaceHandler

    在DubboNamespaceHandler的初始化方法中,Dubbo为每个节点注册了相同的处理类DubboBeanDefinitionParser,但需要注意,它们的beanClass不同。
    public class DubboNamespaceHandler extends NamespaceHandlerSupport {
        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 AnnotationBeanDefinitionParser());
        }
    } 
    所以,在调用handler.parse()方法时,实际调用的是父类的方法。父类方法中,通过配置文件节点的名称,找到对应的处理类,实际调用parse。那么在Dubbo中,大部分调用到的就是DubboBeanDefinitionParser.parse()
    public abstract class NamespaceHandlerSupport implements NamespaceHandler {
    
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            return findParserForElement(element, parserContext).parse(element, parserContext);
        }
        
        private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
            //获取配置文件中的节点名称
            String localName = parserContext.getDelegate().getLocalName(element);
            //找到对应的处理类返回
            BeanDefinitionParser parser = this.parsers.get(localName);
            return parser;
        }
    } 
    看完这个过程,我们再回到开头引用的官网上那句话,就已经明白了。Dubbo就是通过这种方式进行加载的。

    二、加载Bean

    上面我们看到Spring已经扫描到Dubbo的配置文件,接下来就是解析并构建BeanDefinition。这块代码比较长,因为它是把所有的节点都放在一个类来处理的,依靠beanClass来确定当前处理的是哪个节点,所以里面充斥着大量的ie else判断。它的作用就是,把配置文件中的每一个标签,封装成BeanDefinition对象,然后处理这个对象的属性和方法,最后注册到容器中,等待Spring在实例化的时候遍历它们。以上一章节生产者端的配置文件为例,它们解析之后,都会封装为BeanDefinition对象,放入beanFactory。
    dubbo_producer1=Root bean: class [com.alibaba.dubbo.config.ApplicationConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, 
    
    dubbo=Root bean: class [com.alibaba.dubbo.config.ProtocolConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, 
    
    com.alibaba.dubbo.config.RegistryConfig=Root bean: class [com.alibaba.dubbo.config.RegistryConfig]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, 
    
    com.viewscenes.netsupervisor.service.InfoUserService=Root bean: class [com.alibaba.dubbo.config.spring.ServiceBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, 
    
    infoUserService=Generic bean: class [com.viewscenes.netsupervisor.service.impl.InfoUserServiceImpl]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [dubbo_provider1.xml]

    三、Bean的实例化

    上一步从配置文件中读取信息,封装成BeanDefinition对象之后,Spring要循环这些Bean,对它们进行实例化和依赖注入。回到Dubbo上来说,配置的每一个节点都对应一个处理类。
    dubbo:application---------对应--------ApplicationConfig.class dubbo:registry---------对应--------RegistryConfig.class dubbo:protocol---------对应--------ProtocolConfig.class dubbo:service---------对应--------ServiceBean.class
    显然,在Spring进行实例化和依赖注入的时候,势必会调用到这些类的方法。而在这些业务方法里,Dubbo就激活了整个框架的各个部件。以dubbo:service为例,它对应的是ServiceBean,它实现了Spring中的不同接口,就是在Spring Bean的不同时期调用方法,最后完成Dubbo的服务暴露。
    package com.alibaba.dubbo.config.spring;
    
    public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, 
            DisposableBean, ApplicationContextAware, 
            ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
    
        public void setApplicationContext(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
            //......
        }
        public void onApplicationEvent(ContextRefreshedEvent event) {
            //服务暴露
            export();  
        }
        public void afterPropertiesSet() throws Exception {
            //类初始化方法
            //......
        }
    } 
    那么,其他的配置节点也都是一样,就是Spring在实例化Bean的时候调用到Dubbo里的代码,完成它们各自的使命。
  • 相关阅读:
    网站安全处理记录
    C#图片截取(1.按照百分比压缩,2.制定大小压缩)、压缩
    reportviewer导出格式控制
    NPOI 读写excel
    asp.net防止刷新时重复提交
    VS Code MySql扩展 最后完全免费版本 4.5.12
    jmeter中HTTP Cookie Manager
    切换webview报错Failed to start Chromedriver session: Failed to get sockets matching: @weblayer_devtools_remote_.*4738
    在指定目录下面带环境变量运行jmeter
    fidder打断点
  • 原文地址:https://www.cnblogs.com/johnvwan/p/15650001.html
Copyright © 2020-2023  润新知