Disconf原始碼分析之啟動過程分析下(2)
接上文,下面是第二次掃描的XML配置。
<bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond" init-method="init" destroy-method="destroy"> </bean>
檢視init()方法,會呼叫DisconfMgr的secondScan()方法。
protected synchronized void secondScan() { // 上面是順序的校驗 try { // 掃描回撥函式 if (scanMgr != null) { scanMgr.secondScan(); } // 注入資料至配置實體中 // 獲取資料/注入/Watch if (disconfCoreMgr != null) { disconfCoreMgr.inject2DisconfInstance(); } } catch (Exception e) { LOGGER.error(e.toString(), e); } isSecondInit = true; }
scanMgr是掃描處理器,呼叫第二次掃描的secondScan()方法。主要處理如下
// 將回調函式例項化並寫入倉庫 ScanDynamicStoreAdapter.scanUpdateCallbacks(scanModel, registry);
ScanDynamicStoreAdapter是動態掃描與Store模組的轉換器。下面主要對回撥函式的處理。
// ScanStaticModel是第一次掃描結束得到的靜態配置儲存的物件 public static void scanUpdateCallbacks(ScanStaticModel scanModel, Registry registry) { // 掃描出來 ScanDynamicModel scanDynamicModel = analysis4DisconfUpdate(scanModel, registry); // 寫到倉庫中 transformUpdateService(scanDynamicModel.getDisconfUpdateServiceInverseIndexMap()); transformPipelineService(scanDynamicModel.getDisconfUpdatePipeline()); }
analysis4DisconfUpdate()會將配置中和回撥相關的配置掃描處理。
private static ScanDynamicModel analysis4DisconfUpdate(ScanStaticModel scanModel, Registry registry) { // 配置項或檔案,DisconfKey通過配置型別和名稱來標記一個配置key Map<DisconfKey, List<IDisconfUpdate>> inverseMap = new HashMap<DisconfKey, List<IDisconfUpdate>>(); // disconfUpdateService是第一次掃描和回撥相關的配置,@DisconfUpdateService註解 Set<Class<?>> disconfUpdateServiceSet = scanModel.getDisconfUpdateService(); for (Class<?> disconfUpdateServiceClass : disconfUpdateServiceSet) { // 回撥對應的引數 DisconfUpdateService disconfUpdateService = disconfUpdateServiceClass.getAnnotation(DisconfUpdateService.class); // 校驗是否有繼承正確,是否繼承IDisconfUpdate if (!ScanVerify.hasIDisconfUpdate(disconfUpdateServiceClass)) { continue; } // 獲取回撥介面例項 IDisconfUpdate iDisconfUpdate = getIDisconfUpdateInstance(disconfUpdateServiceClass, registry); if (iDisconfUpdate == null) { continue; } // 主要邏輯,將DisconfKey作為key、回撥介面作為list vlaue,存入到inverseMap中 // 配置項 processItems(inverseMap, disconfUpdateService, iDisconfUpdate); // // 配置檔案 processFiles(inverseMap, disconfUpdateService, iDisconfUpdate); } // set data,儲存所有和回撥相關配置結果集合 ScanDynamicModel scanDynamicModel = new ScanDynamicModel(); scanDynamicModel.setDisconfUpdateServiceInverseIndexMap(inverseMap); // // set update pipeline,實現iDisconfUpdatePipeline介面的處理 // if (scanModel.getiDisconfUpdatePipeline() != null) { IDisconfUpdatePipeline iDisconfUpdatePipeline = getIDisconfUpdatePipelineInstance(scanModel .getiDisconfUpdatePipeline(), registry); if (iDisconfUpdatePipeline != null) { // 儲存到scanDynamicModel中 scanDynamicModel.setDisconfUpdatePipeline(iDisconfUpdatePipeline); } } return scanDynamicModel; }
返回以後,會將結果儲存到Store倉庫中。兩種回撥方式分別處理。
transformUpdateService(scanDynamicModel.getDisconfUpdateServiceInverseIndexMap()); transformPipelineService(scanDynamicModel.getDisconfUpdatePipeline());
private static void transformUpdateService(Map<DisconfKey, List<IDisconfUpdate>> disconfUpdateServiceInverseIndexMap) { // 分別取出配置檔案倉庫和配置項倉庫處理器 DisconfStoreProcessor disconfStoreProcessorFile = DisconfStoreProcessorFactory.getDisconfStoreFileProcessor(); DisconfStoreProcessor disconfStoreProcessorItem = DisconfStoreProcessorFactory.getDisconfStoreItemProcessor(); for (DisconfKey disconfKey : disconfUpdateServiceInverseIndexMap.keySet()) { try { if (disconfKey.getDisConfigTypeEnum().equals(DisConfigTypeEnum.FILE)) { // 如果是檔案,第一次靜態掃描結束後,肯定會有對應的配置值 if (!disconfStoreProcessorFile.hasThisConf(disconfKey.getKey())) { throw new Exception(); } // 儲存到倉庫的回撥函式屬性中 disconfStoreProcessorFile.addUpdateCallbackList(disconfKey.getKey(), disconfUpdateServiceInverseIndexMap .get(disconfKey)); } else if (disconfKey.getDisConfigTypeEnum().equals(DisConfigTypeEnum.ITEM)) { // 配置項 if (!disconfStoreProcessorItem.hasThisConf(disconfKey.getKey())) { throw new Exception(); } // 儲存到倉庫的回撥函式屬性中 disconfStoreProcessorItem.addUpdateCallbackList(disconfKey.getKey(), disconfUpdateServiceInverseIndexMap .get(disconfKey)); } } catch (Exception e) { // 找不到回撥對應的配置,這是使用者配置 錯誤了 StringBuffer sb = new StringBuffer(); sb.append("cannot find " + disconfKey + "for: "); for (IDisconfUpdate serClass : disconfUpdateServiceInverseIndexMap.get(disconfKey)) { sb.append(serClass.toString() + "\t"); } LOGGER.error(sb.toString()); } } }
對於pipeline回撥函式類似的處理。
繼續第二次掃描。
// 注入資料至配置實體中 // 獲取資料/注入/Watch if (disconfCoreMgr != null) { disconfCoreMgr.inject2DisconfInstance(); }
該方法的處理,會分別處理File和item兩項,分別呼叫DisconfCoreProcessor實現類(和第一次掃描處理類似)
for (DisconfCoreProcessor disconfCoreProcessor : disconfCoreProcessorList) { disconfCoreProcessor.inject2Conf(); }
下面已File處理為例分析:
inject2Conf()的處理邏輯。
Object object; try { object = disconfCenterFile.getObject(); if (object == null) { // 從上下文獲取例項 object = registry.getFirstByType(disconfCenterFile.getCls(), false, true); } } catch (Exception e) { LOGGER.error(e.toString()); object = null; } // 注入實體中 disconfStoreProcessor.inject2Instance(object, fileName);
繼續向下看。
@Override public void inject2Instance(Object object, String fileName) { // 先取出配置儲存物件 DisconfCenterFile disconfCenterFile = getInstance().getConfFileMap().get(fileName); // 校驗是否存在 if (disconfCenterFile == null) { LOGGER.error("cannot find " + fileName + " in store...."); return; } // // 非靜態類 // if (object != null) { // 設定object disconfCenterFile.setObject(object); } // 根據型別設定值 // // 注入實體 // Map<String, FileItemValue> keMap = disconfCenterFile.getKeyMaps(); for (String fileItem : keMap.keySet()) { // 根據型別設定值 try { // // 靜態類 // if (object == null) { if (keMap.get(fileItem).isStatic()) { LOGGER.debug(fileItem + " is a static field. "); keMap.get(fileItem).setValue4StaticFileItem(keMap.get(fileItem).getValue()); } // // 非靜態類 // } else { LOGGER.debug(fileItem + " is a non-static field. "); if (keMap.get(fileItem).getValue() == null) { // 如果倉庫值為空,則例項 直接使用預設值 Object defaultValue = keMap.get(fileItem).getFieldDefaultValue(object); keMap.get(fileItem).setValue(defaultValue); } else { // 如果倉庫裡的值為非空,則例項使用倉庫裡的值 keMap.get(fileItem).setValue4FileItem(object, keMap.get(fileItem).getValue()); } } } catch (Exception e) { LOGGER.error("inject2Instance fileName " + fileName + " " + e.toString(), e); } } }
分別對靜態和非靜態物件屬性賦值。
到這裡位置第二次掃描結束了。
通過兩次掃描載入的資料,都是通過註解式的分散式配置方式,Disconf同時支援XML非註解式配置方式,在上篇介紹的時候,我們留下了關於XML載入的配置處理的分析,下面分析下XML非註解式配置的原始碼。
對於非註解式配置,Disconf主要區分為properties檔案和非properties檔案(properties檔案才支援自動reload)、是否自動載入reload到bean物件中(通過XML配置決定)。
我們先分析支援自動reload。
<!-- 使用託管方式的disconf配置(無程式碼侵入, 配置更改會自動reload)--> <bean id="configproperties_disconf" class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean"> <property name="locations"> <list> <value>classpath:/autoconfig.properties</value> <value>classpath:/autoconfig2.properties</value> </list> </property> </bean> <bean id="propertyConfigurer" class="com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer"> <property name="ignoreResourceNotFound" value="true" /> <property name="ignoreUnresolvablePlaceholders" value="true" /> <property name="propertiesArray"> <list> <ref bean="configproperties_disconf"/> </list> </property> </bean>
解讀上面的配置前,先了解下Spring提供的PropertyPlaceholderConfigurer類 ,它支援將properties檔案中的配置項讀取並在XML中通過#{}的方式讀取,他的觸發是因為實現了BeanFactoryPostProcessor介面,擴充套件了postProcessBeanFactory方法。
而Disconf就是在此基礎上繼續擴充套件,ReloadingPropertyPlaceholderConfigurer繼承了PropertyPlaceholderConfigurer類。
首先看下ReloadablePropertiesFactoryBean類,它繼承了PropertiesLoaderSupport類,入口是setLocations()方法。
public void setLocations(List<String> fileNames) { List<Resource> resources = new ArrayList<Resource>(); for (String filename : fileNames) { // trim filename = filename.trim(); String realFileName = getFileName(filename); // // register to disconf // 開始掃描,可以參考上文檔案和配置項的掃描 // DisconfMgr.getInstance().reloadableScan(realFileName); // // only properties will reload // String ext = FilenameUtils.getExtension(filename); if (ext.equals("properties")) { PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver(); try { Resource[] resourceList = pathMatchingResourcePatternResolver.getResources(filename); for (Resource resource : resourceList) { resources.add(resource); } } catch (IOException e) { e.printStackTrace(); } } } this.locations = resources.toArray(new Resource[resources.size()]); lastModified = new long[locations.length]; super.setLocations(locations); }
reloadableScan()方法如下:
public synchronized void reloadableScan(String fileName) { if (!isFirstInit) { return; } if (DisClientConfig.getInstance().ENABLE_DISCONF) { try { // 判斷是不是忽略同步的檔案 if (!DisClientConfig.getInstance().getIgnoreDisconfKeySet().contains(fileName)) { if (scanMgr != null) { // 掃描配置 scanMgr.reloadableScan(fileName); } if (disconfCoreMgr != null) { // 核心處理器處理 disconfCoreMgr.processFile(fileName); } LOGGER.debug("disconf reloadable file: {}", fileName); } } catch (Exception e) { LOGGER.error(e.toString(), e); } } }
scanMgr.reloadableScan()會執行StaticScannerNonAnnotationFileMgrImpl.scanData2Store();
,注意StaticScannerNonAnnotationFileMgrImpl
,在上文中,我們介紹了,掃描工具主要包括三種:檔案、配置項、非註解配置。在第一次掃描的時候,非註解配置因為list為空(詳細看上文),所以沒有執行後面的邏輯。
繼續看非註解掃描工具處理。
public static void scanData2Store(String fileName) { // 組裝倉庫物件,和註解檔案的不同在於,檔案註解本身就是一個物件,非註解配置是一個配置檔案,需要轉換為物件。 // 組裝的過程,存在disconfCenterFile.setIsTaggedWithNonAnnotationFile(true);設定。 // 在第一次掃描的時候,有提到,如果是非註解的,該屬性會覆蓋之前註解的倉庫物件 DisconfCenterBaseModel disconfCenterBaseModel = StaticScannerNonAnnotationFileMgrImpl.getDisconfCenterFile(fileName); // 因為非註解配置肯定檔案,所以呼叫檔案倉庫處理器,後面的邏輯參考上文DisconfStoreProcessorFactory.getDisconfStoreFileProcessor().transformScanData(disconfCenterBaseModel); }
掃描完成以後,開始核心處理器處理。
/** * 只處理某一個 */ @Override public void processFile(String fileName) { // 獲取配置檔案核心處理器,原理和上文一樣 DisconfCoreProcessor disconfCoreProcessorFile = DisconfCoreProcessorFactory.getDisconfCoreProcessorFile(watchMgr, fetcherMgr, registry); // 在第一次掃描的時候會呼叫processAllItems()處理,但是xml配置的掃描肯定是單個的,所以直接呼叫單個處理 disconfCoreProcessorFile.processOneItem(fileName); }
再後面的處理和第一次掃描的處理是同一個方法,一個配置檔案, 下載、注入到倉庫、Watch 三步驟。
繼續XML配置解析,對於properties型別的檔案,Spring的PropertyPlaceholderConfigurer類支援處理,所以最後將properties型別的檔案設定到父類的locations屬性中。setLocations()結束。
因為ReloadablePropertiesFactoryBean繼承自PropertiesFactoryBean,PropertiesFactoryBean實現了InitializingBean介面,所以在初始化的時候,會呼叫afterPropertiesSet()方法。
public final void afterPropertiesSet() throws IOException { if(this.singleton) { this.singletonInstance = this.createProperties(); } } protected Properties createProperties() throws IOException { return this.mergeProperties(); }
而ReloadablePropertiesFactoryBean過載了createProperties()方法。
@Override protected Properties createProperties() throws IOException { return (Properties) createMyInstance(); } /** * @throws IOException */ protected Object createMyInstance() throws IOException { // would like to uninherit from AbstractFactoryBean (but it's final!) if (!isSingleton()) { throw new RuntimeException("ReloadablePropertiesFactoryBean only works as singleton"); } // set listener reloadableProperties = new ReloadablePropertiesImpl(); if (preListeners != null) { reloadableProperties.setListeners(preListeners); } // reload reload(true); // add for monitor ReloadConfigurationMonitor.addReconfigurableBean((ReconfigurableBean) reloadableProperties); return reloadableProperties; }
首先看ReloadablePropertiesImpl的實現,他繼承自ReloadablePropertiesBase,包含了List<IReloadablePropertiesListener> listeners
監聽列表。開始preListeners預設為null,直接執行reload(true)
,預設情況下reload方法通過判斷配置檔案的修改時間來確認是否重新載入,這裡因為傳參為true,所以強制reload()
。呼叫ReloadablePropertiesBase的setProperties()。
/** * 通過listener去通知 reload * * @param oldProperties */ protected void notifyPropertiesChanged(Properties oldProperties) { PropertiesReloadedEvent event = new PropertiesReloadedEvent(this, oldProperties); for (IReloadablePropertiesListener listener : listeners) { listener.propertiesReloaded(event); } } /** * set value 觸發 * * @param properties */ protected void setProperties(Properties properties) { Properties oldProperties = internalProperties; synchronized(this) { internalProperties = properties; } notifyPropertiesChanged(oldProperties); }
可以看到最後會遍歷前面所說的listeners列表,如果有值的情況會呼叫listener的propertiesReloaded()方法去reload。IReloadablePropertiesListener介面的實現類是ReloadingPropertyPlaceholderConfigurer 。
有了ReloadablePropertiesFactoryBean以後,Disconf支援兩種非註解式處理,分別的Spring自帶的PropertyPlaceholderConfigurer和ReloadingPropertyPlaceholderConfigurer。兩者的區別是會不會自動reload。結合上面所說,如果想要自動reload,就是通過listeners列表實現。如果使用Spring自帶的PropertyPlaceholderConfigurer,那麼自然就不會有listener。
當我們使用ReloadingPropertyPlaceholderConfigurer的作為XML配置時,因為實現了InitializingBean介面,所以會執行afterPropertiesSet()。
/** * afterPropertiesSet * 將自己 新增 property listener */ public void afterPropertiesSet() { for (Properties properties : propertiesArray) { if (properties instanceof ReloadableProperties) { logger.debug("add property listener: " + properties.toString()); // addReloadablePropertiesListener執行了listeners.add()。 ((ReloadableProperties) properties).addReloadablePropertiesListener(this); } } }
在載入ReloadablePropertiesFactoryBean的時候,我們已經把所有的properties格式的檔案放入到propertiesArray,所以都會加入到listener中,最後會呼叫propertiesReloaded()進行處理。
至此,Disconf的啟動過程分析結束。
轉載請註明出處。
作者:wuxiwei
出處:https://www.cnblogs.com/wxw16/p/10741202.html