如图所示,applicationContext.xml文件中注入了3个和disconf相关的bean。在容器启动的时候,这3个bean将被实例化。
该阶段导入配置,包括:
(1)导入系统配置disconf_sys.properties的数据,如果存在系统参数或命令行导入,则覆盖properties文件导入的数据。
(2)校验系统配置
(3)导入用户配置disconf.properties的数据,如果存在系统参数或命令行导入,则覆盖properties文件导入的数据。
(4)校验用户配置
这样,DisClientConfig对象实例就有了disconf_sys.properties和disconf.properties的参数值。
该阶段主要下载配置文件并将参数注入到仓库中。
/**
* 第一次扫描,静态扫描 for annotation config
*/
protected synchronized void firstScan(List scanPackageList) {
// 该函数不能调用两次
if (isFirstInit) {
LOGGER.info("DisConfMgr has been init, ignore........");
return;
}
try {
// 导入配置
ConfigMgr.init();
LOGGER.info("******************************* DISCONF START FIRST SCAN *******************************");
// registry
Registry registry = RegistryFactory.getSpringRegistry(applicationContext);
// 扫描器
scanMgr = ScanFactory.getScanMgr(registry);
// 第一次扫描并入库
scanMgr.firstScan(scanPackageList);
// 获取数据/注入/Watch
disconfCoreMgr = DisconfCoreFactory.getDisconfCoreMgr(registry);
disconfCoreMgr.process(true);
//
isFirstInit = true;
LOGGER.info("******************************* DISCONF END FIRST SCAN *******************************");
} catch (Exception e) {
LOGGER.error(e.toString(), e);
throw new RuntimeException(e);
}
}
(1)构造一个扫描器scanMgr,staticScannerMgrList维护了配置文件扫描器、配置项扫描器、非注解托管的配置文件扫描器。
(2)进行第一次扫描,扫描scanPackage路径下的加了注解的DisconfFile class,DisconfFileItem method,DisconfItem method等信息。ScanStaticModel分组存储这些扫描信息,再分析出配置文件与配置文件中的Field的Method的MAP–disconfFileItemMap。
(3)通过静态方法StaticScannerFileMgrImpl.transformScanFile转换配置文件,返回对象disconfCenterFile,内容包括disConfCommonModel(app,env,version),url,fields和keyMaps等。
(4)获取配置文件仓库算子,transformScanData方法调用storeOneFile方法存储一个配置文件。
/**
* 存储 一个配置文件
*/
public void storeOneFile(DisconfCenterBaseModel disconfCenterBaseModel) {
DisconfCenterFile disconfCenterFile = (DisconfCenterFile) disconfCenterBaseModel;
String fileName = disconfCenterFile.getFileName();
if (confFileMap.containsKey(fileName)) {
String errorMessage = "There are two same fileName key!!!! " + "first: " + confFileMap.get(fileName).toString() +
"\n, Second: " + disconfCenterFile.toString();
LOGGER.error(errorMessage);
// 不允许出现两个同名的文件
throw new IllegalStateException(errorMessage);
} else {
confFileMap.put(fileName, disconfCenterFile);
}
}
(5)遍历执行processOneItem方法更新一个配置文件,三步骤:获取远程的所有配置数据、注入到仓库中、进行Watch 配置。
注:
1.开启disconf才需要远程下载, 否则就本地就好。注入到仓库操作从设定目录下读取文件异常后, 会尝试从download目录下读取。
2.开启disconf才需要进行watch。
/**
* 第一次扫描
* 在Spring内部的Bean定义初始化后执行,这样是最高优先级的
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 为了做兼容
DisconfCenterHostFilesStore.getInstance().addJustHostFileSet(fileList);
List scanPackList = StringUtil.parseStringToStringList(scanPackage, SCAN_SPLIT_TOKEN);
// unique
Set hs = new HashSet();
hs.addAll(scanPackList);
scanPackList.clear();
scanPackList.addAll(hs);
// 进行扫描
DisconfMgr.getInstance().setApplicationContext(applicationContext);
DisconfMgr.getInstance().firstScan(scanPackList);
// register java bean
registerAspect(registry);
}
该阶段处理非注解托管的配置文件。
/**
* reloadable config file scan, for xml config
*/
public synchronized void reloadableScan(String fileName, String appName, String version) {
if (!isFirstInit) {
return;
}
// 不开启disconf也可以使用本地配置进行注册中中央仓库
// if (DisClientConfig.getInstance().ENABLE_DISCONF) {
try {
if (!DisClientConfig.getInstance().getIgnoreDisconfKeySet().contains(fileName)) {
if (scanMgr != null) {
scanMgr.reloadableScan(fileName, appName, version);
}
if (disconfCoreMgr != null) {
disconfCoreMgr.processFile(fileName);
}
LOGGER.debug("disconf reloadable file: {}", fileName);
}
} catch (Exception e) {
LOGGER.error(e.toString(), e);
throw new RuntimeException(e);
}
// }
}
(1)通过静态方法StaticScannerNonAnnotationFileMgrImpl.getDisconfCenterFile返回配置文件disconfCenterFile,内容包括disConfCommonModel(app,env,version),url等。
(2)获取配置文件仓库算子,transformScanData方法调用storeOneFile方法存储一个配置文件。
(3)执行processOneItem方法更新一个配置文件,三步骤:获取远程的所有配置数据、注入到仓库中、进行Watch 配置。
注:
1.开启disconf才需要远程下载, 否则就本地就好。注入到仓库操作从设定目录下读取文件异常后, 会尝试从download目录下读取。
2.开启disconf才需要进行watch。
3.流程基本和firstScan相似,部分源码参照上面,不再赘述。
4.非注解托管的配置文件依然需要org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer去替换xml文件中的占位符。
该阶段主要做了两件事情:
(1)将回调函数实例化并写入仓库。关于disconf回调
/**
* 扫描更新回调函数
*/
public static void scanUpdateCallbacks(ScanStaticModel scanModel, Registry registry) {
// 扫描出来
ScanDynamicModel scanDynamicModel = analysis4DisconfUpdate(scanModel, registry);
// 写到仓库中
transformUpdateService(scanDynamicModel.getDisconfUpdateServiceInverseIndexMap());
transformPipelineService(scanDynamicModel.getDisconfUpdatePipeline());
}
(2)将仓库confFileMap里的数据注入到配置项、配置文件的实体中。
/**
* 为某个配置文件进行注入实例中
*/
private void inject2OneConf(String fileName, DisconfCenterFile disconfCenterFile) {
if (disconfCenterFile == null) {
return;
}
try {
//
// 获取实例
//
if (!disconfCenterFile.isTaggedWithNonAnnotationFile()) {
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);
}
} catch (Exception e) {
LOGGER.warn(e.toString(), e);
}
}