在前面的文章中,咱們講解了Dubbo是如何建立Provider Bean的(Dubbo之provider bean註冊詳解),其本質就是爲每個使用<dubbo:service/>聲明的接口都使用一個ServiceBean
進行封裝。本文主要講解ServiceBean
是如何爲每個provider bean初始化其默認配置的,以便爲後續的服務暴露作準備的java
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
,那麼其就不必定支持事件監聽機制;addListener()
方法進行的,在最新的版本中已經移除了該方法。 因爲通常用戶是不會修改默認使用的ApplicationContext
的,於是大多數狀況下,Dubbo仍是使用事件監聽機制來導出服務。dom
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
上述屬性讀取完成以後,最終會調用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()
方法就是暴露服務的主要方法。該方法中主要完成了三部分的工做:
本文主要講解了Dubbo在進行服務暴露工做以前的屬性加載和配置的相關工做,而且咱們也對Dubbo所暴露的服務進行了簡要介紹。在後面的文章中,咱們將繼續深刻介紹Dubbo是如何進行服務暴露的。