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
- PHP源码分析之启动过程
- Android源码分析-Activity的启动过程
- 源码分析Android SystemServer进程的启动过程
- (Spring源码解析)一步一步分析,springMVC项目启动过程(二)
- lguest 源码分析之guest os启动的过程
- Android进程启动过程源码分析整理.
- WinCE 6.0启动过程源码分析
- cocos2d-x整体框架源码分析以及启动过程原理(win32)
- Flume-ng 1.6启动过程源码分析(一)
- [Spring Boot] 1. Spring Boot启动过程源码分析
- mina3源码分析,启动过程分析(一)
- Spring之SpringMVC(源码)启动初始化过程分析
- Symfony2源码分析——启动过程1
- Elasticsearch2.4学习(四)------源码分析之启动过程
- Android 5.0源码分析---Content Provider的启动过程分析
- HBase源码分析_Master启动过程
- Zico源码分析:运行启动过程分析和总结
- spring源码分析-应用启动过程
- nginx源码分析(10)-启动过程分析
- Uboot启动过程源码分析之第一阶段(硬件相关)