调试dubbo-1

  1. spring启动解析dubbo的bean定义

    ​ 在dubbo-config-spring 的resources/META-INF/spring.handlers文件中,指定了dubbo的配置解析器, 用来解析dubbo配置文件中的那些标签。key是spring配置文件中的schemaLocation指定的,value是解析器类,一一对应。

    ​ spring容器启动阶段加载配置文件中bean定义的时候(org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)中),会根据标签的namespaceUri获取对应的NamespaceHandler,再用handler解析xml形式的bean定义。

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

    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

    中, 如果首次加载handlerMappings,会扫描所有jar包下的META-INF/spring.handlers,保存在DefaultNamespaceHandlerResolver的Map<String, Object> handlerMappings中。

    spring.handlers

    http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    

    dubbo-spring配置文件

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    
  2. spring创建所有非懒加载bean,dubbo SPI自适应扩展类加载

    上面已经加载了spring配置文件中dubbo相关bean定义,每种dubbo标签都会被解析为一个BeanDefintion, 例如下面这个声明服务接口的配置

    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
    

    会被解析为beanName为com.alibaba.dubbo.demo.DemoService,beanClass为com.alibaba.dubbo.config.spring.ServiceBean 的 BeanDefintion,而ServiceBean的继承关系如下,

    package com.alibaba.dubbo.config.spring;
    
    public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
          
          
    	//...
    }
    

    其中ServiceConfig的静态属性有一句

    public class ServiceConfig<T> extends AbstractServiceConfig {
          
          
    
        private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    // ......
    

    在spring容器启动阶段创建所有非懒加载bean(finishBeanFactoryInitialization(beanFactory) 方法中)时,静态属性初始化,执行getAdaptiveExtension() 方法会加载所有 @SPI注解的类,并缓存起来。诸如此类的加载的类还有Protocol、Cluster、LoadBalance 等。这样就和源码导读里的 Dubbo SPI串联起来了。

    http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html

    总的来说Dubbo SPI的流程就是: 根据指定类型的完整包路径,拼装SPI配置文件路径,加载这些路径指定的文件中,文件的键为需要加载的类的名称(duboo中的url携带的参数),值为这个类的位置,反射创建类后缓存。

    例如加载协议时,类型为com.alibaba.dubbo.rpc.Protocol,拼装后的SPI配置文件路径有三个

    META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
    META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
    META-INF/services/com.alibaba.dubbo.rpc.Protocol
    

    加载到这三个路径下所有键值对,假如我们url中指定的协议类型是dubbo,那就反射创建值对应的类作为协议实现类。

    dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    

    **既然SPI已经可以加载指定的类,为什么还要自适应扩展:**有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。通过自适应类代理,真正调用方法的时候再去使用SPI加载真正实现类。这就需要生成代理类代码。

    http://dubbo.apache.org/zh-cn/docs/source_code_guide/adaptive-extension.html

  3. 创建bean后设置bean的非静态属性

    ​ 上面ServiceBean的静态属性已经加载完了,接下来就是非静态属性的初始化。

    ​ 首先是调用bean的各种*Aware接口,BeanNameAware设备bean名称,ApplicationContextAware设置this.applicationContext属性,并且反射调用org.springframework.context.support.AbstractApplicationContext#addApplicationListener方法,把ServiceBean作为一个ApplicationListener添加到spring容器的监听器列表中,ServiceBean已经实现了ApplicationListener接口,可以作为一个容器事件的监听器。

    public void setApplicationContext(ApplicationContext applicationContext) {
          
          
        this.applicationContext = applicationContext;
        SpringExtensionFactory.addApplicationContext(applicationContext);
            // ....省略
                Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{
          
          ApplicationListener.class}); // backward compatibility to spring 2.0.1
                method.invoke(applicationContext, new Object[]{
          
          this});
        // supportedApplicationListener 置为 true, 这个变量在服务导出的时候会用做是否导出服务的条件。ServiceBean 是 Dubbo 与 Spring 框架进行整合的关键,可以看做是两个框架之间的桥梁。
        		supportedApplicationListener = true;
        // ....省略
    }
    

    ​ 接下来afterPropertiesSet中,把dubbo配置文件中其余的配置信息解析出来,设置到ServiceBean(继承自AbstractInterfaceConfig)的属性上。包括<dubbo:application name=“demo-provider”/>对应的ApplicationConfig,<dubbo:registry address=“multicast://224.5.6.7:1234”/>对应的RegistryConfig,<dubbo:protocol name=“dubbo” port=“20880”/>对应的ProtocolConfig等。

    // 省略了很多代码
    public void afterPropertiesSet() throws Exception {
          
          
            if (getApplication() == null
                    && (getProvider() == null || getProvider().getApplication() == null)) {
          
          
                        setApplication(applicationConfig);
            }
            if (getModule() == null
                    && (getProvider() == null || getProvider().getModule() == null)) {
          
          
                        setModule(moduleConfig);
            }
            if ((getRegistries() == null || getRegistries().size() == 0)
                    && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
                    && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
          
          
                        super.setRegistries(registryConfigs);
            }
            if (getMonitor() == null
                    && (getProvider() == null || getProvider().getMonitor() == null)
                    && (getApplication() == null || getApplication().getMonitor() == null)) {
          
          
                        setMonitor(monitorConfig);
            }
            if ((getProtocols() == null || getProtocols().size() == 0)
                    && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
          
          
                        super.setProtocols(protocolConfigs);
            }
        }
    
  4. spring容器启动bean创建完后发布容器刷新事件,准备dubbo服务导出

    spring容器启动bean创建完后发布容器刷新事件,org.springframework.context.support.AbstractApplicationContext#finishRefresh

    protected void finishRefresh() {
          
          
    	// ....
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        // ....
    }
    

    ServiceBean 作为一个事件监听器,在上面3中已经加入spring容器的监听器列表中。

    onApplicationEvent方法收到事件后,判断如果不是延迟导出服务,并且还没有导出,则调用了export(); 方法导出生产者服务。

    public void onApplicationEvent(ContextRefreshedEvent event) {
          
          
        if (isDelay() && !isExported() && !isUnexported()) {
          
          
            // .....
            export();
        }
    }
    

    如此便进入服务导出的逻辑,http://dubbo.apache.org/zh-cn/docs/source_code_guide/export-service.html

猜你喜欢

转载自blog.csdn.net/u013041642/article/details/109103551