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
Disconf源码分析之启动过程分析下(2)的更多相关文章
- Disconf源码分析之启动过程分析上(1)
Disconf的启动,主要是包括两次扫描和XML非注解式配置,总共分为上下两篇,上篇先主要介绍第一次静态扫描过程. 先从入口分析,通过Disconf帮助文档,可以看到xml必须添加如下配置. < ...
- u-boot 源码分析(1) 启动过程分析
u-boot 源码分析(1) 启动过程分析 文章目录 u-boot 源码分析(1) 启动过程分析 前言 配置 源码结构 api arch board common cmd drivers fs Kbu ...
- Appium Server 源码分析之启动运行Express http服务器
通过上一个系列Appium Android Bootstrap源码分析我们了解到了appium在安卓目标机器上是如何通过bootstrap这个服务来接收appium从pc端发送过来的命令,并最终使用u ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...
- Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...
- v87.01 鸿蒙内核源码分析 (内核启动篇) | 从汇编到 main () | 百篇博客分析 OpenHarmony 源码
本篇关键词:内核重定位.MMU.SVC栈.热启动.内核映射表 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 鸿蒙内核源码分析(汇编基础) | ...
- 死磕以太坊源码分析之MPT树-下
死磕以太坊源码分析之MPT树-下 文章以及资料请查看:https://github.com/blockchainGuide/ 上篇主要介绍了以太坊中的MPT树的原理,这篇主要会对MPT树涉及的源码进行 ...
随机推荐
- using Sysyem.Net.Http命名空间引用不了的解决方案
1.查看.Net Framework的框架是否是在4.5之上,如果不是要下载4.5之上的目标框架. 2.在引用器里面添加using System.Net.Http命名空间 选择项目列表中的“引用”-- ...
- 记录opencv编译过程
准备学习opencv,参考了几个网页终于完成.编辑器和opencv版本都选择最新的版本. 记录过程如下 1. 下载准备: 1) Opencv源码, 下载地址: https://sour ...
- 关于docker jenkins启动时失败的问题处理
最近在做持续集成,然后使用docker 运行jenkins docker run -d -p 8088:8080 -p 50000:50000 -v /home/docker/jenkins_hom ...
- vue 源码学习三 vue中如何生成虚拟DOM
vm._render 生成虚拟dom 我们知道在挂载过程中, $mount 会调用 vm._update和vm._render 方法,vm._updata是负责把VNode渲染成真正的DOM,vm._ ...
- HTML入门10
目前,掌握了图像,视频和音频的嵌入,下面来谈iframe和embed.object嵌入网页, 嵌入简史,刚开始流行用嵌入框架然后不同部分显示i不同内容,可以解决下载速度慢时的问题: 慢慢的插件技术流行 ...
- s:if 判断 s:property
判断<s:property value="XXX"/> 是否是空字符串 则:<s:if test=" XXX == '' ">< ...
- Puppeteer 应用容器化
Puppeteer 应用容器化 Intro Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库.可以通过Puppeteer的提供的api直接控 ...
- 琐事集 vol 2
vol 2-0 宝宝,你是不是该看书咯? 她正瘫在沙发上看剧 我刚提起看书,她惊恐地看了看我 然后眼白一翻,彻底地瘫平了 宝宝? “宝宝睡着了.” 你下周就要考护师了!很难得,她认真地睁开眼,信誓旦旦 ...
- 分门别类总结Java中的各种锁,让你彻底记住
概念 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁.有可能,会造成优先级反转或者饥 ...
- 记录一次JavaWeb开发的乱码解决
POST提交的中文,测试能正确接收到,而且在控制台打印出中文 但是存到数据库乱码 查看了数据库,设置的是utf-8,最后发现应该在数据库连接的地方设置: jdbc:mysql://localhost: ...