在工作中遇到了一个需求,在后端提供一个接口来主动校验配置文件内容格式是否正确。
思路:debug nacos源码 学习nacos底层是怎么实现的。
nacos中有一个com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData
private List<PropertySource<?>> loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
data = configService.getConfig(dataId, group, timeout);
if (StringUtils.isEmpty(data)) {
log.warn(
"Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
dataId, group);
return Collections.emptyList();
}
if (log.isDebugEnabled()) {
log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data));
}
//解析过程
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
fileExtension);
}
catch (NacosException e) {
log.error("get data from Nacos error,dataId:{} ", dataId, e);
}
catch (Exception e) {
log.error("parse data from Nacos error,dataId:{},data:{}", dataId, data, e);
}
return Collections.emptyList();
}
com.alibaba.cloud.nacos.parser.NacosDataParserHandler#parseNacosData
public List<PropertySource<?>> parseNacosData(String configName, String configValue,
String extension) throws IOException {
if (StringUtils.isEmpty(configValue)) {
return Collections.emptyList();
}
if (StringUtils.isEmpty(extension)) {
extension = this.getFileExtension(configName);
}
for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) {
if (!canLoadFileExtension(propertySourceLoader, extension)) {
continue;
}
//封装配置内容信息
NacosByteArrayResource nacosByteArrayResource;
if (propertySourceLoader instanceof PropertiesPropertySourceLoader) {
// PropertiesPropertySourceLoader internal is to use the ISO_8859_1,
// the Chinese will be garbled, needs to transform into unicode.
nacosByteArrayResource = new NacosByteArrayResource(
NacosConfigUtils.selectiveConvertUnicode(configValue).getBytes(),
configName);
}
else {
nacosByteArrayResource = new NacosByteArrayResource(
configValue.getBytes(), configName);
}
nacosByteArrayResource.setFilename(getFileName(configName, extension));
//调用load方法进行解析
List<PropertySource<?>> propertySourceList = propertySourceLoader
.load(configName, nacosByteArrayResource);
if (CollectionUtils.isEmpty(propertySourceList)) {
return Collections.emptyList();
}
return propertySourceList.stream().filter(Objects::nonNull)
.map(propertySource -> {
if (propertySource instanceof EnumerablePropertySource) {
String[] propertyNames = ((EnumerablePropertySource) propertySource)
.getPropertyNames();
if (propertyNames != null && propertyNames.length > 0) {
Map<String, Object> map = new LinkedHashMap<>();
Arrays.stream(propertyNames).forEach(name -> {
map.put(name, propertySource.getProperty(name));
});
return new OriginTrackedMapPropertySource(
propertySource.getName(), map, true);
}
}
return propertySource;
}).collect(Collectors.toList());
}
return Collections.emptyList();
}
自己模仿nacos写了一个简单的解析方法
public class CheckConfigFormatImpl implements CheckConfigFormat {
Map<String,PropertySourceLoader> propertySourceLoaderMap;
@PostConstruct
public void init() {
propertySourceLoaderMap = new ConcurrentHashMap<>();
YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
propertySourceLoaderMap.put("yml",yamlPropertySourceLoader);
propertySourceLoaderMap.put("yaml",yamlPropertySourceLoader);
NacosJsonPropertySourceLoader nacosJsonPropertySourceLoader = new NacosJsonPropertySourceLoader();
propertySourceLoaderMap.put("json",nacosJsonPropertySourceLoader);
PropertiesPropertySourceLoader propertySourceLoader = new PropertiesPropertySourceLoader();
propertySourceLoaderMap.put("properties",propertySourceLoader);
}
@Override
public Result<?> checkContentValid(ConfigRequest.PublishApplicationConfigRequestDTO request) {
if(!propertySourceLoaderMap.containsKey(request.getType())){
return Result.ok().setErrorMsg(String.format("暂时无法解析 %s 文件",request.getType()));
}
PropertySourceLoader propertySourceLoader = propertySourceLoaderMap.get(request.getType());
NacosByteArrayResource resource = new NacosByteArrayResource(NacosConfigUtils.selectiveConvertUnicode(request.getContent()).getBytes());
try {
propertySourceLoader.load(request.getDataId(), resource);
} catch (IOException e) {
return Result.ok().setErrorMsg(e.getMessage());
}
return Result.ok();
}
}
nacos底层的思想还是将各种配置文件转化成map形式的。