美文网首页架构社区Java技术升华我爱编程
☆聊聊Dubbo(四):核心源码-切入Spring

☆聊聊Dubbo(四):核心源码-切入Spring

作者: 七寸知架构 | 来源:发表于2018-03-27 15:39 被阅读366次

    0 前言

    现在大多数Java应用都离不开Spring,所以其他Java解决方案,或多或少都会支持在Spring中使用,Dubbo也不例外。

    正如《聊聊Dubbo(二):简单入门》中的示例,Dubbo在使用上可以做到非常简单,不管是Provider还是Consumer都可以通过Spring的配置文件进行配置,配置完之后,就可以像使用Spring Bean一样进行服务暴露和调用了,完全看不到Dubbo API的存在。

    那么问题来了,Spring是如何识别Dubbo的那些自定义标签的?这就是因为Dubbo使用了Spring提供的可扩展Schema自定义配置支持。

    1 Spring的Schema扩展

    用过Spring就知道可以在xml文件中进行如下配置:

    <context:component-scan base-package="com.demo.dubbo.server.serviceimpl"/>
    <context:property-placeholder location="classpath:config.properties"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    

    Spring是如何来解析这些配置呢?如果我们想自己定义配置该如何做呢?对于上述的xml配置,分成三个部分:

    1. 命名空间namespace,如tx、context
    2. 元素element,如component-scan、property-placeholder、annotation-driven
    3. 属性attribute,如base-package、location、transaction-manager

    Spring定义了两个接口,来分别解析上述内容:

    1. NamespaceHandler:注册了一堆BeanDefinitionParser,利用他们来进行解析;
    2. BeanDefinitionParser: 用于解析每个element的内容;

    来看下具体的一个案例,就以Spring的context命名空间为例,对应的NamespaceHandler实现是ContextNamespaceHandler

    public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    
        @Override
        public void init() {
            registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
            registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
            registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
            registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
            registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
            registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
            registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
        }
    
    }
    

    注册了一堆BeanDefinitionParser,如果我们想看"component-scan"是如何实现的,就可以去看对应的ComponentScanBeanDefinitionParser的源码了;

    如果自定义了NamespaceHandler,如何加入到Spring中呢? Spring默认会在加载jar包下的 META-INF/spring.handlers文件下寻找NamespaceHandler,默认的Spring文件如下:

    META-INF/spring.handlers

    spring.handlers文件内容如下:相应的命名空间使用相应的NamespaceHandler

    http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
    http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
    http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
    http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
    http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
    

    2 Dubbo切入Spring

    2.1 XML转化beanDefinition

    根据Spring可扩展Schema,我们先去dubbo.jar内的META-INF/spring.handlers配置内容:

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

    我们从这个类(DubboNamespaceHandler)开刀吧,DubboNamespaceHandler代码:

    public class DubboNamespaceHandler extends NamespaceHandlerSupport {
        static {
            // 确保系统中只存在一份解析处理器类定义
            Version.checkDuplicate(DubboNamespaceHandler.class);
        }
        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));
        }
    }
    

    按照Spring提供的机制,Dubbo把每个自定义的可使用配置元素和对应的解析器绑定到一起。而真正负责把配置文件中声明的内容解析成对应的BeanDefinition(可以想象为Bean的模子)是靠DubboBeanDefinitionParser.parse类完成,所有dubbo的标签,都统一用DubboBeanDefinitionParser进行解析,基于一对一属性映射,将XML标签解析为Bean对象。

        public BeanDefinition parse(Element element, ParserContext parserContext) {
            return parse(element, parserContext, beanClass, required);
        }
        
        @SuppressWarnings("unchecked")
        private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition();
            beanDefinition.setBeanClass(beanClass);
            beanDefinition.setLazyInit(false);
            String id = element.getAttribute("id");
            if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
            generatedBeanName = beanClass.getName();
            }
                id = generatedBeanName; 
                int counter = 2;
                while(parserContext.getRegistry().containsBeanDefinition(id)) {
                    id = generatedBeanName + (counter ++);
                }
            }
            if (id != null && id.length() > 0) {
                if (parserContext.getRegistry().containsBeanDefinition(id))  {
            throw new IllegalStateException("Duplicate spring bean id " + id);
            }
                parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("id", id);
            }
            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);
            }
            Set<String> props = new HashSet<String>();
            ManagedMap parameters = null;
            for (Method setter : beanClass.getMethods()) {
                String name = setter.getName();
                if (name.length() > 3 && name.startsWith("set")
                        && Modifier.isPublic(setter.getModifiers())
                        && setter.getParameterTypes().length == 1) {
                    Class<?> type = setter.getParameterTypes()[0];
                    String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
                    props.add(property);
                    Method getter = null;
                    try {
                        getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e) {
                        try {
                            getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                        } catch (NoSuchMethodException e2) {
                        }
                    }
                    if (getter == null 
                            || ! Modifier.isPublic(getter.getModifiers())
                            || ! type.equals(getter.getReturnType())) {
                        continue;
                    }
                    if ("parameters".equals(property)) {
                        parameters = parseParameters(element.getChildNodes(), beanDefinition);
                    } else if ("methods".equals(property)) {
                        parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
                    } else if ("arguments".equals(property)) {
                        parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
                    } else {
                        String value = element.getAttribute(property);
                        if (value != null) {
                        value = value.trim();
                        if (value.length() > 0) {
                        if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                                RegistryConfig registryConfig = new RegistryConfig();
                                registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                                beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
                                } else if ("registry".equals(property) && value.indexOf(',') != -1) {
                        parseMultiRef("registries", value, beanDefinition, parserContext);
                                } else if ("provider".equals(property) && value.indexOf(',') != -1) {
                                parseMultiRef("providers", value, beanDefinition, parserContext);
                                } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
                                    parseMultiRef("protocols", value, beanDefinition, parserContext);
                                } else {
                                    Object reference;
                                    if (isPrimitive(type)) {
                                        if ("async".equals(property) && "false".equals(value)
                                                || "timeout".equals(property) && "0".equals(value)
                                                || "delay".equals(property) && "0".equals(value)
                                                || "version".equals(property) && "0.0.0".equals(value)
                                                || "stat".equals(property) && "-1".equals(value)
                                                || "reliable".equals(property) && "false".equals(value)) {
                                            // 兼容旧版本xsd中的default值
                                            value = null;
                                        }
                                        reference = value;
                                    } else if ("protocol".equals(property) 
                                            && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
                                            && (! parserContext.getRegistry().containsBeanDefinition(value)
                                                    || ! ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                        if ("dubbo:provider".equals(element.getTagName())) {
                                            logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
                                        }
                                        // 兼容旧版本配置
                                        ProtocolConfig protocol = new ProtocolConfig();
                                        protocol.setName(value);
                                        reference = protocol;
                                    } else if ("monitor".equals(property) 
                                            && (! parserContext.getRegistry().containsBeanDefinition(value)
                                                    || ! MonitorConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                        // 兼容旧版本配置
                                        reference = convertMonitor(value);
                                    } else if ("onreturn".equals(property)) {
                                        int index = value.lastIndexOf(".");
                                        String returnRef = value.substring(0, index);
                                        String returnMethod = value.substring(index + 1);
                                        reference = new RuntimeBeanReference(returnRef);
                                        beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
                                    } else if ("onthrow".equals(property)) {
                                        int index = value.lastIndexOf(".");
                                        String throwRef = value.substring(0, index);
                                        String throwMethod = value.substring(index + 1);
                                        reference = new RuntimeBeanReference(throwRef);
                                        beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
                                    } else {
                                        if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                            BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                            if (! refBean.isSingleton()) {
                                                throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value+ "\" scope=\"singleton\" ...>");
                                            }
                                        }
                                        reference = new RuntimeBeanReference(value);
                                    }
                            beanDefinition.getPropertyValues().addPropertyValue(property, reference);
                                }
                        }
                        }
                    }
                }
            }
            NamedNodeMap attributes = element.getAttributes();
            int len = attributes.getLength();
            for (int i = 0; i < len; i++) {
                Node node = attributes.item(i);
                String name = node.getLocalName();
                if (! props.contains(name)) {
                    if (parameters == null) {
                        parameters = new ManagedMap();
                    }
                    String value = node.getNodeValue();
                    parameters.put(name, new TypedStringValue(value, String.class));
                }
            }
            if (parameters != null) {
                beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
            }
            return beanDefinition;
        }
    

    2.2 beanDefinition转化Bean

    beanDefinition转化bean的过程其实都是有Spring来完成的,这部分是属于Spring的内容,下图大体描述了Spring内部是如何初始化bean:


    Spring获取Bean实例

    2.3 ServiceBean实例

    其实ServiceBean实例最终都会被转换成字符串以URL的形式交给Dubbo的底层最终暴露成服务。 我们来看下ServiceBean实现的相关接口:

    public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware
    

    这里我们着重看InitializingBean接口,该接口为spring留给开发者的一个hook,用来执行初始化bean的个性化逻辑的回调。

    既然我们的ServiceBean实现了这个接口,意味着当spring进行容器初始化任务过程中,会执行我们在ServiceBean.afterPropertiesSet方法中安排的逻辑,这也是bean导出为服务的关键入口

         @SuppressWarnings({ "unchecked", "deprecation" })
         public void afterPropertiesSet() throws Exception {
             if (getProvider() == null) {    //如果当前serviceBean并没有指定provider,则下面的逻辑为其指定默认的providerConfig(如果存在的话)
                 Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
                 if (providerConfigMap != null && providerConfigMap.size() > 0) {
                     Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                     if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                             && providerConfigMap.size() > 1) { // 兼容旧版本
                         List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                         for (ProviderConfig config : providerConfigMap.values()) {
                             if (config.isDefault() != null && config.isDefault().booleanValue()) {  //把所有指定为默认范围的providerConfig拿到,跳转到下面
                                 providerConfigs.add(config);
                             }
                         }
                         if (providerConfigs.size() > 0) {
                             setProviders(providerConfigs);  //接着上面,把所有指定为默认范围的providerConfig中与protocol相关的配置封装成protocolConfig并存入serviceConfig对应属性中
                         }
                     } else {
                         ProviderConfig providerConfig = null;
                         for (ProviderConfig config : providerConfigMap.values()) {
                             //如果某个provider配置包含子node(ServiceBean),且没有明确指定default,也会被当成默认配置么?这个疑问请参看:com\alibaba\dubbo\config\spring\schema\DubboBeanDefinitionParser.java中330行注解
                             if (config.isDefault() == null || config.isDefault().booleanValue()) {
                                 if (providerConfig != null) {   //只能有一个provider设置为默认
                                     throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                                 }
                                 providerConfig = config;
                             }
                         }
                         if (providerConfig != null) {
                             setProvider(providerConfig);    //为serviceBean绑定继承的providerConfig
                         }
                     }
                 }
             }
             //如果当前serviceBean并没有指定Application且其继承的provider也没有指定Application,则下面的逻辑为其指定默认的applicationConfig(如果存在的话)
             if (getApplication() == null
                     && (getProvider() == null || getProvider().getApplication() == null)) {
                 Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
                 if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                     ApplicationConfig applicationConfig = null;
                     for (ApplicationConfig config : applicationConfigMap.values()) {
                         if (config.isDefault() == null || config.isDefault().booleanValue()) {
                             if (applicationConfig != null) {    //只能有一个Application设置为默认
                                 throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                             }
                             applicationConfig = config;
                         }
                     }
                     if (applicationConfig != null) {
                         setApplication(applicationConfig);  //为serviceBean绑定applicationConfig
                     }
                 }
             }
             //如果当前serviceBean并没有指定Module且其继承的provider也没有指定Module,则下面的逻辑为其指定默认的moduleConfig(如果存在的话)
             if (getModule() == null
                     && (getProvider() == null || getProvider().getModule() == null)) {
                 Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
                 if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                     ModuleConfig moduleConfig = null;
                     for (ModuleConfig config : moduleConfigMap.values()) {
                         if (config.isDefault() == null || config.isDefault().booleanValue()) {
                             if (moduleConfig != null) { //只能有一个Module设置为默认
                                 throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                             }
                             moduleConfig = config;
                         }
                     }
                     if (moduleConfig != null) {
                         setModule(moduleConfig);    //为serviceBean绑定moduleConfig
                     }
                 }
             }
             //如果当前serviceBean并没有指定Registry且其继承的provider,application也没有指定Registry,则下面的逻辑为其指定默认的registryConfig(如果存在的话)
             if ((getRegistries() == null || getRegistries().size() == 0)
                     && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
                     && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
                 Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
                 if (registryConfigMap != null && registryConfigMap.size() > 0) {
                     List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                     for (RegistryConfig config : registryConfigMap.values()) {
                         if (config.isDefault() == null || config.isDefault().booleanValue()) {  //允许为serviceBean指定多个Registry
                             registryConfigs.add(config);
                         }
                     }
                     if (registryConfigs != null && registryConfigs.size() > 0) {
                         super.setRegistries(registryConfigs);
                     }
                 }
             }
             //如果当前serviceBean并没有指定Monitor且其继承的provider,application也没有指定Monitor,则下面的逻辑为其指定默认的monitorConfig(如果存在的话)
             if (getMonitor() == null
                     && (getProvider() == null || getProvider().getMonitor() == null)
                     && (getApplication() == null || getApplication().getMonitor() == null)) {
                 Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
                 if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                     MonitorConfig monitorConfig = null;
                     for (MonitorConfig config : monitorConfigMap.values()) {
                         if (config.isDefault() == null || config.isDefault().booleanValue()) {
                             if (monitorConfig != null) {    //只能有一个Monitor设置为默认
                                 throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                             }
                             monitorConfig = config;
                         }
                     }
                     if (monitorConfig != null) {
                         setMonitor(monitorConfig);  //为serviceBean绑定monitorConfig
                     }
                 }
             }
             //如果当前serviceBean并没有指定Protocol且其继承的provider也没有指定Protocol,则下面的逻辑为其指定默认的protocolConfig(如果存在的话)
             if ((getProtocols() == null || getProtocols().size() == 0)
                     && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
                 Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                 if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                     List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
                     for (ProtocolConfig config : protocolConfigMap.values()) {
                         if (config.isDefault() == null || config.isDefault().booleanValue()) {
                             protocolConfigs.add(config);    //允许为serviceBean指定多个Protocol
                         }
                     }
                     if (protocolConfigs != null && protocolConfigs.size() > 0) {
                         super.setProtocols(protocolConfigs);
                     }
                 }
             }
             //设置服务路径,默认使用的是该bean在spring容器中注册的beanName,这也是该类继承BeanNameAware的原因
             if (getPath() == null || getPath().length() == 0) {
                 if (beanName != null && beanName.length() > 0 
                         && getInterface() != null && getInterface().length() > 0
                         && beanName.startsWith(getInterface())) {
                     setPath(beanName);
                 }
             }
             //若不是延迟加载,就上演好戏
             if (! isDelay()) {
                 export();
             }
         }
    

    这里就明白为何ServiceBean和其父类ServiceConfig不在同一个包内,因为前者是为了适配Spring而提供的适配器。ServiceBean依赖Spring提供的相关hook接口完成了bean的初始化,最终export逻辑交给ServiceConfig来完成,这才是Dubbo的核心服务配置类

    2.4 ServiceConfig

    我们继续跟着线索来看一下ServiceConfig.export:

    public synchronized void export() {
        //从provider中继承一些必要但没有明确设置的参数
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
        if (export != null && ! export.booleanValue()) {    //如果不需要暴露该服务,则就此结束
            return;
        }
        if (delay != null && delay > 0) {   //如果明确指定了想要延迟的时间差,则依赖线程休眠来完成延迟暴露,delay的值只有为-1或null才依赖spring的事件机制完成延迟暴露
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (Throwable e) {
                    }
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            doExport();
        }
    }
    

    一目了然,这个方法主要就是解决了到底暴露不暴露的问题,并且到底是不是延迟暴露的问题。接下来看看doExport方法:

    protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("Already unexported!");
        }
        if (exported) {
            return;
        }
        exported = true;    //修改暴露状态
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
        checkDefault(); //根据文档中提到的参数优先级,决定最终使用的配置值,在spring的xml解析阶段只是简单解析xml的配置值,在真正使用前,还需要看一下:-D和properties文件
        //下面根据文档中的优先级创建对应的继承链
        if (provider != null) { //todo 这里必然成立吧?
            if (application == null) {
                application = provider.getApplication();
            }
            if (module == null) {
                module = provider.getModule();
            }
            if (registries == null) {
                registries = provider.getRegistries();
            }
            if (monitor == null) {
                monitor = provider.getMonitor();
            }
            if (protocols == null) {
                protocols = provider.getProtocols();
            }
        }
        if (module != null) {
            if (registries == null) {
                registries = module.getRegistries();
            }
            if (monitor == null) {
                monitor = module.getMonitor();
            }
        }
        if (application != null) {
            if (registries == null) {
                registries = application.getRegistries();
            }
            if (monitor == null) {
                monitor = application.getMonitor();
            }
        }
        if (ref instanceof GenericService) {    //泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
            interfaceClass = GenericService.class;
            if (StringUtils.isEmpty(generic)) {
                generic = Boolean.TRUE.toString();
            }
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);  //检查接口和方法的匹配情况
            checkRef(); //检查接口和实现的匹配情况
            generic = Boolean.FALSE.toString();
        }
        if(local !=null){   //todo 文档中并没有与local相关的参数解释
            if(local=="true"){
                local=interfaceName+"Local";
            }
            Class<?> localClass;
            try {
                localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(localClass)){
                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
            }
        }
        //本地存根,http://alibaba.github.io/dubbo-doc-static/Stub+Proxy-zh.htm
        if(stub !=null){
            if(stub=="true"){
                stub=interfaceName+"Stub";  //todo 这里文档中的解释貌似有错误:http://alibaba.github.io/dubbo-doc-static/Service+Config-zh.htm
            }
            Class<?> stubClass;
            try {
                stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(stubClass)){
                throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
            }
        }
        //作用雷同于上面的checkDefault(),根据文档中提到的参数优先级来选择使用的配置参数
        checkApplication();
        checkRegistry();
        checkProtocol();
        appendProperties(this);
        checkStubAndMock(interfaceClass);   //检查local,stub和mock的有效性
        if (path == null || path.length() == 0) {   //此时path如果还为空,这使用interfaceName
            path = interfaceName;
        }
        doExportUrls();
    }
    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);  //获取所有的注册中心地址
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "dubbo"; //N多次的检查,N多次的赋值,这算是严谨呢?还是重复?
        }
        String host = protocolConfig.getHost();
        if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
        }
        boolean anyhost = false;
        if (NetUtils.isInvalidLocalHost(host)) {    //检查host是否为本地ip,或者无效的
            anyhost = true;
            try {
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
            if (NetUtils.isInvalidLocalHost(host)) {    //如果拿到的还是本地地址,就只能出杀手锏了
                if (registryURLs != null && registryURLs.size() > 0) {
                    for (URL registryURL : registryURLs) {
                        try {
                            Socket socket = new Socket();
                            try {
                                //尝试连接注册中心,选用连接时使用的ip地址
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();
                                break;
                            } finally {
                                try {
                                    socket.close();
                                } catch (Throwable e) {}
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
                if (NetUtils.isInvalidLocalHost(host)) {
                    host = NetUtils.getLocalHost(); //实在不行,就只能使用本机上第一个找到的合法ip了
                }
            }
        }
        Integer port = protocolConfig.getPort();
        if (provider != null && (port == null || port == 0)) {
            port = provider.getPort();
        }
        final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
        if (port == null || port == 0) {
            port = defaultPort;
        }
        if (port == null || port <= 0) {
            port = getRandomPort(name);
            if (port == null || port < 0) {
                port = NetUtils.getAvailablePort(defaultPort);  //到这里如果还没有拿到port,就直接随机拿个能用的端口
                putRandomPort(name, port);  //这一步很讲究,意味着相同协议使用相同的端口,要理解这个就需要先消化dubbo底层通信方式。
            }
            logger.warn("Use random available port(" + port + ") for protocol " + name);
        }
        Map<String, String> map = new HashMap<String, String>();
        if (anyhost) {
            map.put(Constants.ANYHOST_KEY, "true");
        }
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        //获取相关的配置参数用于后面的url生成,注意优先级顺序哟
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);
        if (methods != null && methods.size() > 0) {
            for (MethodConfig method : methods) {
                appendParameters(map, method, method.getName());
                //处理重试设置
                String retryKey = method.getName() + ".retry";
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    if ("false".equals(retryValue)) {
                        map.put(method.getName() + ".retries", "0");
                    }
                }
                List<ArgumentConfig> arguments = method.getArguments();
                if (arguments != null && arguments.size() > 0) {
                    for (ArgumentConfig argument : arguments) { //ArgumentConfig作用主要就是用来完成事件回调机制。
                        //类型自动转换.
                        if(argument.getType() != null && argument.getType().length() >0){
                            Method[] methods = interfaceClass.getMethods();
                            //遍历所有方法
                            if(methods != null && methods.length > 0){
                                for (int i = 0; i < methods.length; i++) {
                                    String methodName = methods[i].getName();
                                    //匹配方法名称,获取方法签名.
                                    if(methodName.equals(method.getName())){    //注意方法重载情况
                                        Class<?>[] argtypes = methods[i].getParameterTypes();
                                        //一个方法中单个callback
                                        if (argument.getIndex() != -1 ){    //todo 这部分和文档写的有出入,不过这也不是第一次了。。
                                            if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
                                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                            }else {
                                                throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {
                                            //一个方法中多个callback
                                            for (int j = 0 ;j<argtypes.length ;j++) {   //todo 这部分和文档写的有出入,不过这也不是第一次了。。
                                                Class<?> argclazz = argtypes[j];
                                                if (argclazz.getName().equals(argument.getType())){
                                                    appendParameters(map, argument, method.getName() + "." + j);
                                                    if (argument.getIndex() != -1 && argument.getIndex() != j){
                                                        throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }else if(argument.getIndex() != -1){
                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                        }else {
                            throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                        }
                    }
                }
            } // end of methods for
        }
        if (ProtocolUtils.isGeneric(generic)) { //处理泛化
            map.put("generic", generic);
            map.put("methods", Constants.ANY_VALUE);
        } else {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);  //todo 为什么是revision?
            }
            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); //todo 动态封装interfaceClass,目前不知道干啥用,猜测dubbo直接操作的都是这个封装后的wrapper
            if(methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put("methods", Constants.ANY_VALUE);
            }
            else {
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        //令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌,否则使用静态令牌,令牌的作用是防止消费者绕过注册中心直接访问,保证注册中心的授权功能有效,如果使用点对点调用,需关闭令牌功能
        if (! ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put("token", UUID.randomUUID().toString());
            } else {
                map.put("token", token);
            }
        }
        //injvm表示不会跨进程,所以不需要注册中心
        if ("injvm".equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }
        // 导出服务
        String contextPath = protocolConfig.getContextpath();
        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);   //拿到服务的url
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }
        String scope = url.getParameter(Constants.SCOPE_KEY);
        //配置为none不暴露
        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
            //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        //todo 暴露为何要封装一层代理呢?
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    //todo 暴露为何要封装一层代理呢?
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);
    }
    

    Dubbo会为每个有效协议暴露一份服务,并且会注册到所有有效的注册中心里。而bean转变为service中最重要的就是映射出来的URL,也就是说我们在配置文件中进行的相关配置都会映射成对应url中的相关部分。

    dubbo://192.168.4.109:20880/com.demo.dubbox.service.order.api.OrderService?accepts=2000&
    anyhost=true&application=hello-world-app-test&default.actives=3000&
    default.connections=5000&default.delay=-1&default.executes=3000&
    default.retries=3&default.timeout=20000&delay=-1&dubbo=2.8.4&generic=false&
    interface=com.demo.dubbox.service.order.api.OrderService&methods=updateOrderByUserID,getOrderByUserID,getServiceIP&
    pid=4716&side=provider&threadpool=cached&threads=2000&timestamp=1464576489808
    

    那么这个url的作用是什么呢?官方给出的解释很明确,这个url作为解耦的通信数据(跨层调用的参数),有了它dubbo就可以更容易做到业务逻辑实现的替换。除此之外可以看到url中还包含了大量的辅助参数(例如:timeout,version,organization等)供服务治理使用。

    相关文章

      网友评论

      本文标题:☆聊聊Dubbo(四):核心源码-切入Spring

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