转载

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

现在大多数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文件如下:

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

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:

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

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等)供服务治理使用。

原文  https://juejin.im/post/5ab9f574518825558a06a4d7
正文到此结束
Loading...