ApplicationContext事件启动监听

在前面的文章中,咱们讲解了Dubbo是如何建立Provider Bean的(Dubbo之provider bean注册详解),其本质就是为每个使用<dubbo:service/>声明的接口都使用一个ServiceBean进行封装。本文主要讲解ServiceBean是如何为每个provider bean初始化其默认配置的,以便为后续的服务暴露作准备的java

1. ApplicationContext事件启动监听

        ServiceBean暴露服务的入口方法是ServiceBean.export()方法,而该方法主要有两个地方在调用:redis

  • ServiceBean.afterPropertySet()获取配置属性以后调用;
  • ServiceBean.onApplicationEvent()中监听ApplicationContext启动完成事件后调用。

        在默认状况下,Dubbo是会经过第二种方式进行服务暴露的,由于这样能够保证Dubbo的启动是在Spring启动以后,也就给予咱们一种能够在暴露过程当中使用已经彻底初始化的Spring服务的功能。至于第一种状况和第二种状况的区别主要在于Dubbo会检测Spring是否支持事件的监听机制,若是支持,则使用第二种方式,不支持则使用第一种机制。具体的检测方式是在ServiceBean.setApplicationContext()方法中进行的:缓存

@Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; // 将ApplicationContext设置到SpringExtensionFactory中,这样就可使得Dubbo的SPI机制 // 得以使用Spring容器中的相关服务功能 SpringExtensionFactory.addApplicationContext(applicationContext); // 检测是否支持事件监听机制,若是支持,则将当前ServiceBean当作一个事件注册到Spring容器中 supportedApplicationListener = addApplicationListener(applicationContext, this); } public static boolean addApplicationListener(ApplicationContext applicationContext, ApplicationListener listener) { try { // 获取ApplicationContext对象中的addApplicationListener()方法, // 而且经过反射调用该方法,从而注册监听事件,若是当前ApplicationContext没有该方法, // 那么这里就会抛出异常 Method method = applicationContext.getClass() .getMethod("addApplicationListener", ApplicationListener.class); method.invoke(applicationContext, listener); return true; } catch (Throwable t) { if (applicationContext instanceof AbstractApplicationContext) { try { // 若是ApplicationContext对象是AbstractApplicationContext类型的,则经过 // 反射调用其addListener()方法,添加监听的事件。 Method method = AbstractApplicationContext.class .getDeclaredMethod("addListener", ApplicationListener.class); if (!method.isAccessible()) { method.setAccessible(true); } method.invoke(applicationContext, listener); return true; } catch (Throwable t2) { // ignore } } } // 若是上述两种方式都不支持,则表示当前ApplicationContext不支持事件监听机制 return false; } 

        关于Dubbo为什么使用这种方式来检查是否支持事件监听机制的缘由有两点:app

  • 上面的addApplicationListener()方法是ApplicationContext接口的子接口ConfigurableApplicationContext的一个方法,于是若是用户使用了自定义的ApplicationContext,那么其就不必定支持事件监听机制;
  • catch语句中的第二种添加方式主要是由于在旧版本中,事件的添加是经过addListener()方法进行的,在最新的版本中已经移除了该方法。

        因为通常用户是不会修改默认使用的ApplicationContext的,于是大多数状况下,Dubbo仍是使用事件监听机制来导出服务。dom

2. afterPropertySet()方式配置属性

        Dubbo的服务配置,不只仅可使用<dubbo:service/>等配置标签的方式来声明所使用的application、registry和protocol,其还可使用声明Spring bean的方式来进行。具体的步骤就是经过ServiceBean.afterPropertySet()方法进行读取的,其代码以下:jvm

@Override public void afterPropertiesSet() throws Exception { // 首先判断当前ServiceBean中是否已经设置了ProviderConfig对象,若是不存在, // 则尝试读取Spring容器中配置的ProviderConfig对象 if (getProvider() == null) { 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); // 上面的代码中首先从容器中获取了ProviderConfig,而后获取了ProtocolConfig,在下面的分支中, // 首先会判断读取到的ProtocolConfig是空的,而且获得的ProviderConfig的数量大于1,这说明 // 其是多协议支持的Provider,这个时候,就会将ProviderConfig转换为ProtocolConfig设置 // 到当前ServiceBean中,须要注意的是,下面的setProviders()方法就是进行这个转换过程的, // 其并不会为当前ServiceBean的ProviderConfig设置任何数据。并且每一个ServiceBean中也只会有 // 一个ProviderConfig if (CollectionUtils.isEmptyMap(protocolConfigMap) && providerConfigMap.size() > 1) { List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>(); for (ProviderConfig config : providerConfigMap.values()) { if (config.isDefault() != null && config.isDefault()) { providerConfigs.add(config); } } if (!providerConfigs.isEmpty()) { setProviders(providerConfigs); // 将ProviderConfig转换为ProtocolConfig保存起来 } } else { // 这个分支说明容器中是存在ProtocolConfig的配置,或者获得的ProviderConfig只有一个, // 那么这里就会将该ProviderConfig设置到ServiceBean中 ProviderConfig providerConfig = null; for (ProviderConfig config : providerConfigMap.values()) { if (config.isDefault() == null || config.isDefault()) { if (providerConfig != null) { throw new IllegalStateException( "Duplicate provider configs: " + providerConfig + " and " + config); } providerConfig = config; } } if (providerConfig != null) { setProvider(providerConfig); } } } } // 若是没有配置ApplicationConfig,而且也没法从ProviderConfig中获取到ApplicationConfig, // 那么就到Spring容器中查找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) { // 从Spring容器中获取ApplicationConfig,而且将该对象设置到ServiceBean中 ApplicationConfig applicationConfig = null; for (ApplicationConfig config : applicationConfigMap.values()) { if (applicationConfig != null) { throw new IllegalStateException( "Duplicate application configs: " + applicationConfig + " and " + config); } applicationConfig = config; } if (applicationConfig != null) { setApplication(applicationConfig); } } } // 若是没有配置ModuleConfig,而且也没法从ProviderConfig中获取到ModuleConfig, // 那么就到Spring容器中查找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) { // 从Spring容器中获取ModuleConfig,而且将该对象设置到ServiceBean中 ModuleConfig moduleConfig = null; for (ModuleConfig config : moduleConfigMap.values()) { if (config.isDefault() == null || config.isDefault()) { if (moduleConfig != null) { throw new IllegalStateException( "Duplicate module configs: " + moduleConfig + " and " + config); } moduleConfig = config; } } if (moduleConfig != null) { setModule(moduleConfig); } } } // 若是没有配置registryIds属性,则从ApplicationConfig中读取该属性,而后从ProviderConfig // 中读取该属性。这里存在一个优先级的关系,若是ApplicationConfig和ProviderConfig中都存在 // 该属性,那么最终将会以ProviderConfig中的为准。 if (StringUtils.isEmpty(getRegistryIds())) { if (getApplication() != null && StringUtils.isNotEmpty(getApplication().getRegistryIds())) { setRegistryIds(getApplication().getRegistryIds()); } if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getRegistryIds())) { setRegistryIds(getProvider().getRegistryIds()); } } // 若是ProviderConfig和ApplicationConfig都没有指定RegistryConfig,那么就从Spring容器中 // 读取,而后判断是否配置了registryIds,若是配置了,则经过registryIds获取RegistryConfig, // 若是没有配置,则将获得的全部RegistryConfig都设置到ServiceBean中 if ((CollectionUtils.isEmpty(getRegistries())) && (getProvider() == null || CollectionUtils.isEmpty(getProvider().getRegistries())) && (getApplication() == null || CollectionUtils.isEmpty(getApplication().getRegistries()))) { Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false); if (CollectionUtils.isNotEmptyMap(registryConfigMap)) { List<RegistryConfig> registryConfigs = new ArrayList<>(); // 若是配置了registryIds,则只获取指定的RegistryConfig if (StringUtils.isNotEmpty(registryIds)) { Arrays.stream(Constants.COMMA_SPLIT_PATTERN.split(registryIds)).forEach(id -> { if (registryConfigMap.containsKey(id)) { registryConfigs.add(registryConfigMap.get(id)); } }); } // 若是经过registryIds没有获取到RegistryConfig,则将全部的RegistryConfig都添加到ServiceBean中 if (registryConfigs.isEmpty()) { for (RegistryConfig config : registryConfigMap.values()) { if (StringUtils.isEmpty(registryIds)) { registryConfigs.add(config); } } } if (!registryConfigs.isEmpty()) { super.setRegistries(registryConfigs); } } } // 若是配置的MetadataConfig为空,则从Spring容器中获取一个MetadataConfig, // 将其设置到ServiceBean中,若是从Spring容器中获取到了多个,则抛出异常 if (getMetadataReportConfig() == null) { Map<String, MetadataReportConfig> metadataReportConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class, false, false); if (metadataReportConfigMap != null && metadataReportConfigMap.size() == 1) { super.setMetadataReportConfig(metadataReportConfigMap.values().iterator().next()); } else if (metadataReportConfigMap != null && metadataReportConfigMap.size() > 1) { throw new IllegalStateException( "Multiple MetadataReport configs: " + metadataReportConfigMap); } } // 若是配置的ConfigCenter为空,则从Spring容器中获取一个ConfigCenter, // 将其设置到ServiceBean中,若是从Spring容器中获取到了多个,则抛出异常 if (getConfigCenter() == null) { Map<String, ConfigCenterConfig> configenterMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConfigCenterConfig.class, false, false); if (configenterMap != null && configenterMap.size() == 1) { super.setConfigCenter(configenterMap.values().iterator().next()); } else if (configenterMap != null && configenterMap.size() > 1) { throw new IllegalStateException("Multiple ConfigCenter found:" + configenterMap); } } // 若是配置的MonitorConfig为空,而且从ProviderConfig和ApplicationConfig中都没法获取到 // MonitorConfig,则从Spring容器中读取,须要注意的是,配置的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()) { if (monitorConfig != null) { throw new IllegalStateException( "Duplicate monitor configs: " + monitorConfig + " and " + config); } monitorConfig = config; } } if (monitorConfig != null) { setMonitor(monitorConfig); } } } // 设置protocolIds属性 if (StringUtils.isEmpty(getProtocolIds())) { if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getProtocolIds())) { setProtocolIds(getProvider().getProtocolIds()); } } // 若是配置的ProtocolConfig为空,则从Spring容器中读取,而后会判断当前Provider是否配置了protocolIds // 属性,若是配置了,则只读取该Ids指定的ProtocolConfig,若是没有配置,则将全部的ProtocolConfig都 // 设置到ServiceBean中 if (CollectionUtils.isEmpty(getProtocols()) && (getProvider() == null || CollectionUtils.isEmpty(getProvider().getProtocols()))) { 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>(); // 经过protocolIds属性来获取配置的ProtocolConfig if (StringUtils.isNotEmpty(getProtocolIds())) { Arrays.stream(Constants.COMMA_SPLIT_PATTERN.split(getProtocolIds())) .forEach(id -> { if (protocolConfigMap.containsKey(id)) { protocolConfigs.add(protocolConfigMap.get(id)); } }); } // 若是获取到的ProtocolConfig为空,则将全部的ProtocolConfig都添加到ServiceBean中 if (protocolConfigs.isEmpty()) { for (ProtocolConfig config : protocolConfigMap.values()) { if (StringUtils.isEmpty(protocolIds)) { protocolConfigs.add(config); } } } if (!protocolConfigs.isEmpty()) { super.setProtocols(protocolConfigs); } } } // 设置path属性,默认为接口的全限定名 if (StringUtils.isEmpty(getPath())) { if (StringUtils.isNotEmpty(beanName) && StringUtils.isNotEmpty(getInterface()) && beanName.startsWith(getInterface())) { setPath(beanName); } } // 这里会判断是否支持事件监听机制,若是不支持,则在这里导出Dubbo服务 if (!supportedApplicationListener) { export(); } } 

        上面的代码中,主要逻辑就是判断配置文件中是否配置了ApplicationConfig、ProviderConfig等对象对应的标签,若是没有配置,则会从Spring容器中读取这些配置,而后将其设置到当前ServiceBean中。最后会判断当前是否支持事件监听机制,若是不支持,就会在最后经过调用export()方法导出Dubbo服务。ide

3. 默认属性配置

        上述属性读取完成以后,最终会调用ServiceBean.export()方法导出服务,可是在导出服务以前,会根据各个层级的配置,来根据优先级配置各个属性值。这里咱们继续阅读export()方法的源码:函数

public synchronized void export() { // 检查各个默认属性的配置,而且按照优先级进行覆盖 checkAndUpdateSubConfigs(); // 检查是否应该导出服务,若是当前正在导出或者已经取消了导出,那么就会在这里被拦截 if (!shouldExport()) { return; } // 这里主要是用于延迟导出的,最终仍是会交由doExport()方法导出服务 if (shouldDelay()) { delayExportExecutor.schedule(this::doExport, delay, TimeUnit.MILLISECONDS); } else { doExport(); } } 

        上面的导出过程,主要分为两个:a. 检查属性的配置,而且根据配置的优先级进行属性的覆盖;b. 调用doExportUrl()导出服务。咱们首先来看checkAndUpdateSubConfigs()是如何进行属性的优先级检查的:ui

public void checkAndUpdateSubConfigs() { // 为ServiceBean设置默认属性,好比当前配置了ProviderConfig,可是没有配置RegistryConfig,那么 // 有可能ProviderConfig中是配置了其所要进行的注册信息的,此时就会经过ProviderConfig获取 // RegistryConfig对象,从而保证RegistryConfig有值。须要说明的是,若是ProviderConfig中 // 也仍是没有配置RegistryConfig,那么RegistryConfig仍是会为空 completeCompoundConfigs(); // 设置ConfigCenterConfig的相关属性,这里本质上实现的工做就是依次经过SystemConfiguration // -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig // -> PropertiesConfiguration的优先级来读取属性配置,而后将其设置到ConfigCenterConfig中 startConfigCenter(); // 检查是否有ProviderConfig的配置,若是不存在,则建立一个 checkDefault(); // 检查是否存在ApplicationConfig的配置,若是不存在,则新建一个 checkApplication(); // 检查是否存在RegistryConfig的配置,若是不存在,则新建一个 checkRegistry(); // 检查是否存在ProtocolConfig的配置,若是不存在,则新建一个 checkProtocol(); // 依次对新建的ProviderConfig,ApplicationConfig,RegistryConfig和ProtocolConfig设置属性, // 设置的方式就是经过读取外部配置的属性值,而后经过调用这些Config对象的setter方法将其设置到各个 // Config类中 this.refresh(); // 检查是否存在MetadataReportConfig,若是不存在,则新建一个,而且调用其refresh()方法设置属性 checkMetadataReport(); if (StringUtils.isEmpty(interfaceName)) { throw new IllegalStateException("<dubbo:service interface=\"\" /> " + "interface not allow null!"); } // 若是当前接口类型是泛华类型,则设置generic属性为true,而且设置interfaceClass为GenericService if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try { // 若是当前配置的class是普通的class,则经过反射读取该class文件 interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } // 这里主要是检查在当前接口中配置的方法名和参数等信息与真实读取到的class文件的方法名和参数信息是否匹配 checkInterfaceAndMethods(interfaceClass, methods); // 这里主要是检查ref属性指向的对象类型是否为所设置的接口的一个实现类 checkRef(); // 此时generic为false,由于其为普通接口 generic = Boolean.FALSE.toString(); } // 若是配置了local属性,那么就会在当前classpath中查找目标接口名加上Local后缀的实现类, // 这个实现类将做为须要暴露的目标服务的一个代理类,这种使用方式已经被废弃,取而代之的是下面的Stub方式 if (local != null) { if ("true".equals(local)) { local = interfaceName + "Local"; } Class<?> localClass; try { // 加载Local代理 localClass = ClassHelper.forNameWithThreadContextClassLoader(local); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } // 检查加载的代理类是否为目标接口的一个实现类 if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException( "The local implementation class " + localClass.getName() + " not implement interface " + interfaceName); } } // 若是配置了stub属性,那么就会在classpath中查找目标接口名加上Stub后缀的实现类, // 这个类将会被做为代理类在客户端使用,其能够对须要代理的服务进行一些自定义的逻辑,好比进行缓存等 if (stub != null) { if ("true".equals(stub)) { stub = interfaceName + "Stub"; } Class<?> stubClass; try { // 加载Stub代理类 stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(www.sengshiyuLe.cn), e); } // 若是加载的代理类不是目标接口的一个实现类,则抛出异常 if (!interfaceClass.isAssignableFrom(stubClass)) { throw new IllegalStateException( "The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName); } } // 因为Local和Stub类都是代理类,于是这里会检查加载的这两个类是否存在以目标接口为参数的构造函数, // 若是不存在,则抛出异常 checkStubAndLocal(interfaceClass); // 检查是否配置了mock属性,若是配置了,则检查该属性是否符合规范。mock属性的做用主要是在目标服务接口抛出 // 异常时,将会使用mock属性所指定的方式来返回虚拟值 checkMock(interfaceClass); } 

        这里checkAndUpdateSubConfigs()方法首先对各个Config类的属性进行覆盖,覆盖方式是根据优先级来读取系统属性,外部配置等属性值,而后将这些属性值依次设置到各个Config类中;而后就是处理Local和Stub类型的代理类,而且检查这些类是否符合规范;最后就是检查mock属性的值是否符合规范。下面咱们来看一下doExport()方法是如何导出目标服务的:this

protected synchronized void doExport() { if (unexported) { throw new IllegalStateException( "The service " + interfaceClass.getName() + " has already unexported!"); } if (exported) { return; } exported = true; if (StringUtils.isEmpty(path)) { path = interfaceName; } // 导出服务 doExportUrls(); } private void doExportUrls() { // 获取各个注册中心的配置,而且将其转换为URL对象 List<URL> registryURLs = loadRegistries(true); for (ProtocolConfig protocolConfig : protocols) { // 获取当前服务的key,其格式为group/interface/version,这个key是用于标识当前服务的一个惟一键 String pathKey = URL.buildKey(getContextPath(www.zongshenpt.cn protocolConfig).map(p -> p + "/" + path).orElse(path), group, version); // 根据服务key,interfaceClass和目标bean构建一个ProviderModel对象,这个ProviderModel // 是对应于Provider的一个封装 ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass); // 将服务key与ProviderModel关联起来 ApplicationModel.initProviderModel(pathKey, providerModel); // 导出服务 doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } 

        在doExportUrls()方法中,主要是依次对各个ProtocolConfig都提供当前Provider的暴露功能,这里的ProtocolConfig其实配置的就是传输使用的协议方式,好比netty或者mina等。咱们继续阅读doExportUrlsFor1Protocol()方法的源码:

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { // 获取协议名称,默认为dubbo String name = protocolConfig.getName(); if (StringUtils.isEmpty(name)) { name = Constants.DUBBO; } // 这里的map中保存的是暴露服务时最终将要使用的属性,其会被转换为一个URL对象,而URL对象是Dubbo在整个 // 服务暴露过程当中所使用的一个传递参数的对象,于是其做用很是重要。在下面的调用中,会依次对 // ApplicationConfig,ModuleConfig和ProviderConfig来执行appendParameters()方法,这个方法的 // 主要做用是经过这些Config类的getter方法获取其属性值,而后将其属性值设置到map中,若是指定了属性前缀, // 那么map中使用的key就是"前缀.属性名"的形式。从这里就能够看出,对于属性的优先级,是以 // ServiceConfig > ProtocolConfig > ProviderConfig > ModuleConfig > ApplicationConfig // 的顺序排列的 Map<String, String> map = new HashMap<String, String>(); map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE); appendRuntimeParameters(map); appendParameters(map, application); appendParameters(map, module); appendParameters(map, provider, Constants.DEFAULT_KEY); appendParameters(map, protocolConfig); appendParameters(map, this); // 这段代码的主要做用是判断配置中对目标接口配置的方法以及方法参数是否与真实的接口的方法和参数匹配 if (CollectionUtils.isNotEmpty(methods)) { 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 (CollectionUtils.isNotEmpty(arguments)) { for (ArgumentConfig argument : arguments) { // convert argument type if (argument.getType() != null && argument.getType().length() > 0) { // 获取真实接口的方法信息 Method[] methods = interfaceClass.getMethods(); // visit all methods if (methods != null && methods.length > 0) { for (int i = 0; i < methods.length; i++) { String methodName = methods[i].getName(); // 判断配置的方法名与真实的方法名是否一致,若是一致,则继续进行方法参数的判断 if (methodName.equals(method.getName(www.jinLiyLd.cn))) { // 获取真实接口的方法参数信息 Class<?>[] argtypes = methods[i].getParameterTypes(); // 若是配置中的参数的index属性有值,说明其指定了该参数在对应的方法中的位置, // 此时能够直接判断呢真实的参数与该配置的参数类型是否一致 if (argument.getIndex() != -1) { if (argtypes[argument.getIndex()].getName() .equals(argument.getType())) { // 方法和参数都匹配,则将方法参数的配置信息设置到map中 appendParameters(map, argument, method.getName() + "." + argument.getIndex()); } else { // 这种状况是配置了方法参数的索引,可是真实方法的该索引位置的参数类型与配置的 // 类型不一致,此时须要抛出异常,由于配置有问题 throw new IllegalArgumentException( "Argument config error : the index attribute and type attribute " + "not match :index :" + argument.getIndex() + ", type:" + argument.getType(www.jingxiupt.cn)); } } else { // 这个分支是方法参数中没有配置index属性,这种状况下直接遍历方法的全部参数, // 判断其与配置的参数类型是否一致 for (int j = 0; j < argtypes.length; j++) { 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 " + "attribute not match :index :" + argument.getIndex(www.chengsyl.cn) + ", type:" + argument.getType()); } } } } } } } } else if (argument.getIndex() != -1) { // 这个分支是配置的参数类型为空,此时就检查其index属性,不为空的时候才进行处理 appendParameters(map, argument, method.getName() + "." + argument.getIndex()); } else { // 当配置的参数类型和index都为空的状况下,抛出异常 throw new IllegalArgumentException( "Argument config must set index or type attribute.eg: <dubbo:argument " + "index='0' .../> or <dubbo:argument type=xxx .../>"); } } } } } // 判断目标类是否为泛化类型,若是是,则设置与泛化相关的属性值 if (ProtocolUtils.isGeneric(generic)) { map.put(Constants.GENERIC_KEY, generic); map.put(Constants.METHODS_KEY, Constants.ANY_VALUE); } else { // 设置接口的版本信息 String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length(www.fengshen157.com) > 0) { map.put("revision", revision); } // 这里的Wrapper类是一个封装类,其做用主要是对须要暴露的接口进行一个统一的封装, // 其形式很是相似于反射,可是其与反射不一样,这里的Wrapper会为目标接口动态生成子类字节码, // 而后经过javassist来编译生成的字节码,最后经过反射获取该类的对象。 String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if (methods.length == 0) { logger.warn("No method found in service interface " + interfaceClass.getName()); map.put(Constants.METHODS_KEY, Constants.ANY_VALUE); } else { // 设置须要暴露的服务的方法信息 map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); } } // 为当前provider设置一个惟一的id if (!ConfigUtils.isEmpty(token)) { if (ConfigUtils.isDefault(token)) { map.put(Constants.TOKEN_KEY, UUID.randomUUID(www.feishenbo.cn).toString()); } else { map.put(Constants.TOKEN_KEY, token); } } // 获取配置的须要导出的ip和端口号信息 String host = this.findConfigedHosts(protocolConfig, registryURLs, map); Integer port = this.findConfigedPorts(protocolConfig, name, map); // 这里,就将全部获得的配置属性和服务信息构建为了一个url对象,该对象将在后面暴露服务中 // 起到传递参数的做用 URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map); // 这里主要是对生成的URL对象的属性进行一些覆盖操做 if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .hasExtension(url.getProtocol())) { url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .getExtension(url.getProtocol()).getConfigurator(url).configure(url); } // 下面主要是进行暴露服务的工做,其主要分为两个部分:injvm和registry。injvm指的是将服务暴露到 // 当前jvm中,在当前jvm中能够进行相关调用。registry指的是根据相关的注册中心配置,如zookeeper或redis, // 将服务信息暴露到对应的注册中心中。须要注意的是,在暴露到注册中心的同时,也会在本地进行一次暴露,经过 // 这种方式,咱们就能够在本地进行服务直连,而不用到注册中心拉取服务 String scope = url.getParameter(Constants.SCOPE_KEY); // 获取scope属性值,若是属性值不为none,则开始进行暴露服务的工做 if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) { if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) { // 若是scope不为remote,则会将服务暴露到当前的jvm中 exportLocal(url); } // 若是scope不为local,则会将服务暴露到配置的注册中心中 if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) { if (logger.isInfoEnabled(www.chaohyl.cn)) { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } if (CollectionUtils.isNotEmpty(registryURLs)) { // 依次遍历各个注册中心的配置,将当前服务注册到注册中心 for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY)); // 获取监控中心的相关配置 URL monitorUrl = loadMonitor(registryURL); if (monitorUrl != null) { url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); } if (logger.isInfoEnabled(www.dasheng178.com  )) { logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); } // 获取生成动态代理的方式,如javassist或jdk String proxy = url.getParameter(Constants.PROXY_KEY); if (StringUtils.isNotEmpty(proxy)) { registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy); } // 根据配置生成Invoker对象,该对象是一个基础服务类,该类抽象了对当前服务的调用 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); // 将生成的Invoker对象进行暴露 Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter); } } else { // 若是没有注册中心相关的配置,则会使用默认值来注册服务,好比注册中心为zookeeper Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter); } // 保存所暴露的服务的一些元数据信息 MetadataReportService metadataReportService = null; if ((metadataReportService = getMetadataReportService()) != null) { metadataReportService.publishProvider(url); } } } this.urls.add(url); } 

        能够看到,这里的doExportUrlsFor1Protocol()方法就是暴露服务的主要方法。该方法中主要完成了三部分的工做:

  • 构建最终的参数map,其属性将会用于暴露服务所用;
  • 校验接口方法配置与须要暴露的方法是否相匹配,若是不匹配,则抛出异常;
  • 对服务进行暴露,主要包含两个部分:injvm和registry,其中registry暴露的时候,也会根据相应的协议在本地进行一次暴露,经过这种方式,咱们就能够实现本地服务的直连。

4. 小结

        本文主要讲解了Dubbo在进行服务暴露工做以前的属性加载和配置的相关工做,而且咱们也对Dubbo所暴露的服务进行了简要介绍。在后面的文章中,咱们将继续深刻介绍Dubbo是如何进行服务暴露的。

相关文章
相关标签/搜索