CSE的配置管理和加载功能在foundation-config模块中实现,完成配置项的加载和处理。
配置文件的加载
foundations\foundation-config\src\main\resources\META-INF\spring\cse.bean.xml文件实现了配置的初始化,文件的内容如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.apache.servicecomb.config.ConfigurationSpringInitializer"/>
<bean class="org.apache.servicecomb.config.DynamicPropertiesImpl"/>
</beans>
此处存在两个初始化入口类,ConfigurationSpringInitializer和DynamicPropertiesImpl。
ConfigurationSpringInitializer
实现配置的加载功能,将动态配置和microservice.yaml加载到spring,properties/.yml加载到archivs.
/**
* Adapt spring PropertySource and Archaius Configuration
* spring vs archaius
* (add) | dynamic(configcenter)
* system property | system property
* environment | environment
* *properties/*.yml | (add)
* (add) | microservice.yaml
*
* add dynamic configuration, microserive.yaml to spring, add *properties/*.yml to archaius
*
* NOTICE: we are not duplicate spring system property and environment property source, this will cause some problem
* related to precedence of a KEY-VAlUE. That is cse.test in dynamic config may not override servicecomb.test in yml.
* Users need to use the same key as what is in config file to override.
*/
// PropertyPlaceholderConfigurer类是将占位符指向的数据库配置信息放在bean中定义的工具
// EnvironmentAware为spring的接口:凡注册到Spring容器内的bean,实现了EnvironmentAware接口重写setEnvironment方法后,在工程启动时可以获得application.properties的配置文件配置的属性值。
public class ConfigurationSpringInitializer extends PropertyPlaceholderConfigurer implements EnvironmentAware {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationSpringInitializer.class);
public static final String EXTRA_CONFIG_SOURCE_PREFIX = "extraConfig-";
public ConfigurationSpringInitializer() {
setOrder(Ordered.LOWEST_PRECEDENCE / 2);
// 无法解析的占位符不报错
setIgnoreUnresolvablePlaceholders(true);
}
/**
* Get configurations from Spring, merge them into the configurations of ServiceComb.
* @param environment From which to get the extra config.
*/
// 重新EnvironmentAware的方法,获取当前的系统配置
@Override
public void setEnvironment(Environment environment) {
// 当前Environment的实现类为StandardEnvironment
String environmentName = generateNameForEnvironment(environment);
LOGGER.info("Environment received, will get configurations from [{}].", environmentName);
Map<String, Object> extraConfig = getAllProperties(environment);
// 此处掉ConfigUtil的静态方法功能
// 对应的key为extraConfig-org.springframework.core.env.StandardEnvironment@1296928719
ConfigUtil.addExtraConfig(EXTRA_CONFIG_SOURCE_PREFIX + environmentName, extraConfig);
// 加载业务配置
ConfigUtil.installDynamicConfig();
// 将当前的配置信息加载到spring中
setUpSpringPropertySource(environment);
}
private void setUpSpringPropertySource(Environment environment) {
// 当前的StanardEnvironment是ConfigurableEnvironment的子类
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment ce = (ConfigurableEnvironment) environment;
// 业务配置加载扩展点
ConfigCenterConfigurationSource configCenterConfigurationSource =
SPIServiceUtils.getTargetService(ConfigCenterConfigurationSource.class);
if (configCenterConfigurationSource != null) {
try {
ce.getPropertySources()
.addFirst(new MapPropertySource("dynamic-source", configCenterConfigurationSource.getCurrentData()));
} catch (Exception e) {
LOGGER.warn("set up spring property source failed. msg: {}", e.getMessage());
}
}
ConcurrentCompositeConfiguration concurrentCompositeConfiguration = ConfigUtil.createLocalConfig();
ce.getPropertySources().addLast(
new EnumerablePropertySource<ConcurrentCompositeConfiguration>("microservice.yaml",
concurrentCompositeConfiguration) {
private String[] propertyNames = null;
@Override
public String[] getPropertyNames() {
if (propertyNames == null) {
List<String> keyList = Lists.newArrayList(this.source.getKeys());
propertyNames = keyList.toArray(new String[keyList.size()]);
}
return propertyNames;
}
@Override
public Object getProperty(String s) {
return this.source.getProperty(s);
}
});
}
}
@Override
protected Properties mergeProperties() throws IOException {
Properties properties = super.mergeProperties();
AbstractConfiguration config = ConfigurationManager.getConfigInstance();
Iterator<String> iter = config.getKeys();
while (iter.hasNext()) {
String key = iter.next();
Object value = config.getProperty(key);
properties.put(key, value);
}
return properties;
}
@Override
protected String resolvePlaceholder(String placeholder, Properties props) {
String propertyValue = super.resolvePlaceholder(placeholder, props);
if (propertyValue == null) {
return DynamicPropertyFactory.getInstance().getStringProperty(placeholder, null).get();
}
return propertyValue;
}
/**
* Try to get a name for identifying the environment.
* @param environment the target that the name is generated for.
* @return The generated name for the environment.
*/
private String generateNameForEnvironment(Environment environment) {
String environmentName = environment.getProperty("spring.config.name");
if (!StringUtils.isEmpty(environmentName)) {
return environmentName;
}
environmentName = environment.getProperty("spring.application.name");
if (!StringUtils.isEmpty(environmentName)) {
return environmentName;
}
// 在CSE的框架中,如上的值一般不会配置,因此走到此处
// 例如某次的返回值如下: org.springframework.core.env.StandardEnvironment@985487390
return environment.getClass().getName() + "@" + environment.hashCode();
}
/**
* Traversal all {@link PropertySource} of {@link ConfigurableEnvironment}, and try to get all properties.
*/
// 从Environment中解析属性
private Map<String, Object> getAllProperties(Environment environment) {
Map<String, Object> configFromSpringBoot = new HashMap<>();
// StandardEnvironment是ConfigurableEnvironment的子类
if (!(environment instanceof ConfigurableEnvironment)) {
return configFromSpringBoot;
}
ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment;
// configurableEnvironment.getPropertySources()中当前包含两个实现
// MapPropertySource和SystemEnvironmentPropertySource
for (PropertySource<?> propertySource : configurableEnvironment.getPropertySources()) {
getProperties(configurableEnvironment, propertySource, configFromSpringBoot);
}
return configFromSpringBoot;
}
/**
* Get property names from {@link EnumerablePropertySource}, and get property value from {@link ConfigurableEnvironment#getProperty(String)}
*/
private void getProperties(ConfigurableEnvironment environment, PropertySource<?> propertySource,
Map<String, Object> configFromSpringBoot) {
if (propertySource instanceof CompositePropertySource) {
// recursively get EnumerablePropertySource
CompositePropertySource compositePropertySource = (CompositePropertySource) propertySource;
compositePropertySource.getPropertySources().forEach(ps -> getProperties(environment, ps, configFromSpringBoot));
return;
}
// 对于MapPropertySource,是EnumerablePropertySource的实现类
// 对于SystemEnvironmentPropertySource,是EnumerablePropertySource的实现类
if (propertySource instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) propertySource;
// 以本地启动的CSE服务为例,enumerablePropertySource.getPropertyNames()中包含的配置项名称为:["java.runtime.name","scb-scan-package","sun.boot.library.path","java.vm.version","java.vm.vendor","java.vendor.url","path.separator","java.vm.name","file.encoding.pkg","user.country","user.script","sun.java.launcher","sun.os.patch.level","java.vm.specification.name","user.dir","intellij.debug.agent","java.runtime.version","java.awt.graphicsenv","java.endorsed.dirs","os.arch","java.io.tmpdir","line.separator","java.vm.specification.vendor","user.variant","os.name","sun.jnu.encoding","java.library.path","jboss.modules.system.pkgs","java.specification.name","java.class.version","sun.management.compiler","os.version","user.home","user.timezone","java.awt.printerjob","file.encoding","java.specification.version","java.class.path","user.name","java.vm.specification.version","sun.java.command","java.home","sun.arch.data.model","user.language","java.specification.vendor","org.apache.logging.log4j.assignedSequences","awt.toolkit","java.vm.info","java.version","java.ext.dirs","sun.boot.class.path","java.vendor","file.separator","java.vendor.url.bug","sun.io.unicode.encoding","sun.cpu.endian","sun.desktop","sun.cpu.isalist"]
// 以本地启动的CSE服务为例,SystemEnvironmentPropertySource中包含的配置项名称为:["configsetroot","USERDOMAIN_ROAMINGPROFILE","GIT_HOME","PROCESSOR_LEVEL","SESSIONNAME","ALLUSERSPROFILE","PROCESSOR_ARCHITECTURE","PSModulePath","SystemDrive","JRE_HOME","SCOOP","MOZ_PLUGIN_PATH","USERNAME","USERDNSDOMAIN","MOSQUITTO_DIR","ProgramFiles(x86)","FPS_BROWSER_USER_PROFILE_STRING","PYTHONPATH","PATHEXT","DriverData","PcAccess","ProgramData","ProgramW6432","HOMEPATH","PROCESSOR_IDENTIFIER","HADOOP_HOME","M2_HOME","ProgramFiles","PUBLIC","windir","=::","LOCALAPPDATA","ChocolateyLastPathUpdate","IntelliJ IDEA","USERDOMAIN","FPS_BROWSER_APP_PROFILE_STRING","LOGONSERVER","JAVA_HOME","GRADLE_HOME","PYSPARK_PYTHON","OneDrive","APPDATA","GRADLE_USER_HOME","SPARK_HOME","ChocolateyInstall","CommonProgramFiles","Path","PyCharm","OS","COMPUTERNAME","PROCESSOR_REVISION","RSKUSERNAME","CLASSPATH","CommonProgramW6432","ComSpec","RADONFILESENCODING","SystemRoot","TEMP","HOMEDRIVE","USERPROFILE","TMP","CommonProgramFiles(x86)","NUMBER_OF_PROCESSORS","IDEA_INITIAL_DIRECTORY"],对应的均为操作系统变量的配置
for (String propertyName : enumerablePropertySource.getPropertyNames()) {
try {
// configFromSpringBoot中存储了从Environment中获取的配置值
configFromSpringBoot.put(propertyName, environment.getProperty(propertyName, Object.class));
} catch (Exception e) {
if (ignoreResolveFailure()) {
LOGGER.warn("set up spring property source failed.", e);
} else {
throw new RuntimeException("set up spring property source failed.If you still want to start up the application and ignore errors, you can set servicecomb.config.ignoreResolveFailure to true.", e);
}
}
}
return;
}
LOGGER.debug("a none EnumerablePropertySource is ignored, propertySourceName = [{}]", propertySource.getName());
}
private boolean ignoreResolveFailure() {
return ConfigUtil
.createLocalConfig()
.getBoolean("servicecomb.config.ignoreResolveFailure", false);
}
}
org.apache.servicecomb.config.ConfigUtil实现了配置管理工具类的功能,实现逻辑如下:
public final class ConfigUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);
private static final String MICROSERVICE_CONFIG_LOADER_KEY = "cse-microservice-config-loader";
private static Map<String, Object> localConfig = new HashMap<>();
/**
* <p>The configurations not read by ServiceComb.</p>
* <p>
* For example, this map can store the configurations read by SpringBoot from application.properties,
* If users write the configurations of ServiceComb into application.yml instead of microservice.yaml,
* this can help {@link ConfigUtil} load config correctly.
* </p>
*/
private static final Map<String, Map<String, Object>> EXTRA_CONFIG_MAP = new LinkedHashMap<>();
private ConfigUtil() {
}
public static void setConfigs(Map<String, Object> config) {
localConfig = config;
}
public static void addConfig(String key, Object value) {
localConfig.put(key, value);
}
public static Object getProperty(String key) {
Object config = DynamicPropertyFactory.getBackingConfigurationSource();
return getProperty(config, key);
}
public static Object getProperty(Object config, String key) {
if (null != config && Configuration.class.isInstance(config)) {
Configuration configuration = (Configuration) config;
return configuration.getProperty(key);
}
return null;
}
private static void setMicroserviceConfigLoader(Configuration config, MicroserviceConfigLoader loader) {
config.setProperty(MICROSERVICE_CONFIG_LOADER_KEY, loader);
}
public static MicroserviceConfigLoader getMicroserviceConfigLoader() {
return (MicroserviceConfigLoader) getProperty(MICROSERVICE_CONFIG_LOADER_KEY);
}
public static MicroserviceConfigLoader getMicroserviceConfigLoader(Configuration config) {
return (MicroserviceConfigLoader) getProperty(config, MICROSERVICE_CONFIG_LOADER_KEY);
}
// 创建本地配置管理对象
public static ConcurrentCompositeConfiguration createLocalConfig() {
// 初始化方法中校验cse.configurationSource.additionalUrls和cse.configurationSource.defaultFileName不能配置值
MicroserviceConfigLoader loader = new MicroserviceConfigLoader();
// 加载完成配置文件
loader.loadAndSort();
if (localConfig.size() > 0) {
ConfigModel model = new ConfigModel();
model.setConfig(localConfig);
loader.getConfigModels().add(model);
}
LOGGER.info("create local config:");
for (ConfigModel configModel : loader.getConfigModels()) {
LOGGER.info(" {}.", configModel.getUrl());
}
// 获取环境变量和服务的配置信息
ConcurrentCompositeConfiguration config = ConfigUtil.createLocalConfig(loader.getConfigModels());
// 将loader的信息放入config中对应的配置中,对应的key为cse-microservice-config-loader
// 在ConcurrentCompositeConfiguration中都会封装为其变量configList中的Configuration来源对象
ConfigUtil.setMicroserviceConfigLoader(config, loader);
return config;
}
// 根据加载的配置项,放入archives中进行管理
public static ConcurrentCompositeConfiguration createLocalConfig(List<ConfigModel> configModelList) {
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
// 将系统配置项包装为ConcurrentMapConfiguration织入ConcurrentCompositeConfiguration
duplicateCseConfigToServicecomb(config,
new ConcurrentMapConfiguration(new SystemConfiguration()),
"configFromSystem");
// 将当前服务的环境配置信息,加入ConcurrentCompositeConfiguration中
// convertEnvVariable将其中包含_的配置项,将配置项名称中的_替换为.,重新放入source中
duplicateCseConfigToServicecomb(config,
convertEnvVariable(new ConcurrentMapConfiguration(new EnvironmentConfiguration())),
"configFromEnvironment");
// If there is extra configurations, add it into config.
// 将spring的环境配置信息加入config中
EXTRA_CONFIG_MAP.entrySet()
.stream()
.filter(mapEntry -> !mapEntry.getValue().isEmpty())
.forEachOrdered(configMapEntry -> duplicateCseConfigToServicecomb(config,
new ConcurrentMapConfiguration(configMapEntry.getValue()),
configMapEntry.getKey()));
// we have already copy the cse config to the serviceComb config when we load the config from local yaml files
// hence, we do not need duplicate copy it.
// 将microservice.yaml的配置值加入config中,由于此处的配置加载是本地配置加载,因此NeverStartPollingScheduler中什么都没做
// MicroserviceConfigurationSource配置信息的加载更新逻辑,此处每次都会将所有的配置重新读取,返回全量的数据
config.addConfiguration(new DynamicConfiguration(
new MicroserviceConfigurationSource(configModelList), new NeverStartPollingScheduler()),
"configFromYamlFile");
// ConfigMapping.getConvertedMap将所有的value,以原因的value为key,原有的value为value新建一个Map
duplicateCseConfigToServicecombAtFront(config,
new ConcurrentMapConfiguration(ConfigMapping.getConvertedMap(config)),
"configFromMapping");
return config;
}
public static AbstractConfiguration convertEnvVariable(AbstractConfiguration source) {
Iterator<String> keys = source.getKeys();
while (keys.hasNext()) {
String key = keys.next();
String[] separatedKey = key.split(CONFIG_KEY_SPLITER);
if (separatedKey.length == 1) {
continue;
}
String newKey = String.join(".", separatedKey);
source.addProperty(newKey, source.getProperty(key));
}
return source;
}
//inject a copy of servicecomb.xxx for cse.xxx
// 此处进行配置项的装换,将cse.开头的配置,换为以servicecomb.开头后重新放入source中
private static void duplicateCseConfigToServicecomb(AbstractConfiguration source) {
Iterator<String> keys = source.getKeys();
while (keys.hasNext()) {
String key = keys.next();
// 只有以cse.开头的配置项才会在此处加载
if (!key.startsWith(CONFIG_CSE_PREFIX)) {
continue;
}
String servicecombKey = CONFIG_SERVICECOMB_PREFIX + key.substring(key.indexOf(".") + 1);
if (!source.containsKey(servicecombKey)) {
source.addProperty(servicecombKey, source.getProperty(key));
} else {
LOGGER
.warn(
"Key {} with an ambiguous item {} exists, it's recommended to use only one of them.",
key, servicecombKey);
}
}
}
private static void duplicateCseConfigToServicecomb(ConcurrentCompositeConfiguration compositeConfiguration,
AbstractConfiguration source,
String sourceName) {
duplicateCseConfigToServicecomb(source);
compositeConfiguration.addConfiguration(source, sourceName);
}
private static void duplicateCseConfigToServicecombAtFront(ConcurrentCompositeConfiguration compositeConfiguration,
AbstractConfiguration source,
String sourceName) {
duplicateCseConfigToServicecomb(source);
compositeConfiguration.addConfigurationAtFront(source, sourceName);
}
private static ConfigCenterConfigurationSource createConfigCenterConfigurationSource(
Configuration localConfiguration) {
// 此处cse提供了扩展点ConfigCenterConfigurationSource,后续进行讨论,运行业务自定义加载自有的配置中心加载对应的配置
ConfigCenterConfigurationSource configCenterConfigurationSource =
SPIServiceUtils.getTargetService(ConfigCenterConfigurationSource.class);
if (null == configCenterConfigurationSource) {
LOGGER.info(
"config center SPI service can not find, skip to load configuration from config center");
return null;
}
if (!configCenterConfigurationSource.isValidSource(localConfiguration)) {
LOGGER.warn("Config Source serverUri is not correctly configured.");
return null;
}
return configCenterConfigurationSource;
}
private static void createDynamicWatchedConfiguration(
ConcurrentCompositeConfiguration localConfiguration,
ConfigCenterConfigurationSource configCenterConfigurationSource) {
ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfiguration();
localConfiguration.addConfigurationAtFront(injectConfig, "extraInjectConfig");
configCenterConfigurationSource.addUpdateListener(new ServiceCombPropertyUpdateListener(injectConfig));
DynamicWatchedConfiguration configFromConfigCenter =
new DynamicWatchedConfiguration(configCenterConfigurationSource);
duplicateCseConfigToServicecomb(configFromConfigCenter);
localConfiguration.addConfigurationAtFront(configFromConfigCenter, "configCenterConfig");
}
public static AbstractConfiguration createDynamicConfig() {
ConcurrentCompositeConfiguration compositeConfig = ConfigUtil.createLocalConfig();
ConfigCenterConfigurationSource configCenterConfigurationSource =
createConfigCenterConfigurationSource(compositeConfig);
if (configCenterConfigurationSource != null) {
createDynamicWatchedConfiguration(compositeConfig, configCenterConfigurationSource);
}
return compositeConfig;
}
public static void installDynamicConfig() {
// 调用archives的ConfigurationManager管理类,业务自定义的配置信息是否已经安装
if (ConfigurationManager.isConfigurationInstalled()) {
LOGGER.warn("Configuration installed by others, will ignore this configuration.");
return;
}
// 加载本地的配置项
ConcurrentCompositeConfiguration compositeConfig = ConfigUtil.createLocalConfig();
// 此处加载ConfigCenterConfigurationSource的实现类
ConfigCenterConfigurationSource configCenterConfigurationSource =
createConfigCenterConfigurationSource(compositeConfig);
if (configCenterConfigurationSource != null) {
createDynamicWatchedConfiguration(compositeConfig, configCenterConfigurationSource);
}
// 将目前的配置加载到ConfigurationManager中
ConfigurationManager.install(compositeConfig);
if (configCenterConfigurationSource != null) {
configCenterConfigurationSource.init(compositeConfig);
}
}
public static void destroyConfigCenterConfigurationSource() {
SPIServiceUtils.getAllService(ConfigCenterConfigurationSource.class).forEach(source -> {
try {
source.destroy();
} catch (Throwable e) {
LOGGER.error("Failed to destroy {}", source.getClass().getName());
}
});
}
public static void addExtraConfig(String extraConfigName, Map<String, Object> extraConfig) {
EXTRA_CONFIG_MAP.put(extraConfigName, extraConfig);
}
public static void clearExtraConfig() {
EXTRA_CONFIG_MAP.clear();
}
private static class ServiceCombPropertyUpdateListener implements WatchedUpdateListener {
private final ConcurrentMapConfiguration injectConfig;
ServiceCombPropertyUpdateListener(ConcurrentMapConfiguration injectConfig) {
this.injectConfig = injectConfig;
}
@Override
public void updateConfiguration(WatchedUpdateResult watchedUpdateResult) {
Map<String, Object> adds = watchedUpdateResult.getAdded();
if (adds != null) {
for (String add : adds.keySet()) {
if (add.startsWith(CONFIG_CSE_PREFIX)) {
String key = CONFIG_SERVICECOMB_PREFIX + add.substring(add.indexOf(".") + 1);
injectConfig.addProperty(key, adds.get(add));
}
}
}
Map<String, Object> deletes = watchedUpdateResult.getDeleted();
if (deletes != null) {
for (String delete : deletes.keySet()) {
if (delete.startsWith(CONFIG_CSE_PREFIX)) {
injectConfig.clearProperty(CONFIG_SERVICECOMB_PREFIX + delete.substring(delete.indexOf(".") + 1));
}
}
}
Map<String, Object> changes = watchedUpdateResult.getChanged();
if (changes != null) {
for (String change : changes.keySet()) {
if (change.startsWith(CONFIG_CSE_PREFIX)) {
String key = CONFIG_SERVICECOMB_PREFIX + change.substring(change.indexOf(".") + 1);
injectConfig.setProperty(key, changes.get(change));
}
}
}
}
}
@SuppressWarnings("unchecked")
public static ConcurrentHashMap<String, DynamicProperty> getAllDynamicProperties() {
try {
return (ConcurrentHashMap<String, DynamicProperty>) FieldUtils
.readDeclaredStaticField(DynamicProperty.class, "ALL_PROPS", true);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
@SuppressWarnings("unchecked")
public static CopyOnWriteArraySet<Runnable> getCallbacks(DynamicProperty property) {
try {
return (CopyOnWriteArraySet<Runnable>) FieldUtils.readDeclaredField(property, "callbacks", true);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
org.apache.servicecomb.config.archaius.sources.MicroserviceConfigLoader进行服务配置的加载:
public class MicroserviceConfigLoader extends YAMLConfigLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceConfigLoader.class);
private static final String ADDITIONAL_CONFIG_URL = "servicecomb.configurationSource.additionalUrls";
private static final String DEFAULT_FILE_NAME = "servicecomb.configurationSource.defaultFileName";
/**
* Default configuration file name to be used by default constructor. This file should
* be on the classpath. The file name can be overridden by the value of system property
* <code>configurationSource.defaultFileName</code>
*/
private static final String DEFAULT_CONFIG_FILE_NAME = "microservice.yaml";
public MicroserviceConfigLoader() {
// Help to resolve incompatible changes. Can be deleted in future.
if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.additionalUrls"))) {
throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls"
+ " has been replaced with -D" + ADDITIONAL_CONFIG_URL + ", please change it and restart.");
}
if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.defaultFileName"))) {
throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls"
+ " has been replaced with -D" + DEFAULT_FILE_NAME + ", please change it and restart.");
}
}
public void loadAndSort() {
try {
// 配置文件的名称,默认为microservice.yaml
String configFileFromClasspath =
System.getProperty(DEFAULT_FILE_NAME) == null ? DEFAULT_CONFIG_FILE_NAME
: System.getProperty(DEFAULT_FILE_NAME);
// 此处初始化了configModels变量
super.load(configFileFromClasspath);
// 加载servicecomb.configurationSource.additionalUrls配置路径的配置值
loadAdditionalConfig();
if (configModels.isEmpty()) {
LOGGER.warn("No URLs will be polled as dynamic configuration sources.");
LOGGER.warn(
"To enable URLs as dynamic configuration sources, define System property {} or make {} available on classpath.",
ADDITIONAL_CONFIG_URL,
configFileFromClasspath);
}
// 按照order进行重新的排序
sort();
} catch (IOException e) {
throw new ServiceCombException("Failed to load microservice config", e);
}
}
private void loadAdditionalConfig() throws IOException {
String strUrls = System.getProperty(ADDITIONAL_CONFIG_URL);
if (StringUtils.isEmpty(strUrls)) {
return;
}
for (String strUrl : strUrls.split(",")) {
URL url = new URL(strUrl);
ConfigModel configModel = load(url);
configModels.add(configModel);
}
}
}
org.apache.servicecomb.config.archaius.sources.YAMLConfigLoader实现了yaml加载的功能:
public class YAMLConfigLoader extends AbstractConfigLoader {
@SuppressWarnings("unchecked")
@Override
protected Map<String, Object> loadData(URL url) throws IOException {
Yaml yaml = new Yaml();
try (InputStream inputStream = url.openStream()) {
return yaml.loadAs(inputStream, Map.class);
}
}
}
YAMLConfigLoader的基类是AbstractConfigLoader,实现逻辑如下:
public abstract class AbstractConfigLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractConfigLoader.class);
private static final String ORDER_KEY = "servicecomb-config-order";
protected final List<ConfigModel> configModels = new ArrayList<>();
public List<ConfigModel> getConfigModels() {
return configModels;
}
public void load(String resourceName) throws IOException {
loadFromClassPath(resourceName);
}
protected void loadFromClassPath(String resourceName) throws IOException {
List<URL> urlList = findURLFromClassPath(resourceName);
for (URL url : urlList) {
ConfigModel configModel = load(url);
configModels.add(configModel);
}
}
// 逐一加载microservice.yaml配置文件
public ConfigModel load(URL url) throws IOException {
// 将microservice.yaml加载为嵌套Map的形式
Map<String, Object> config = loadData(url);
// load a empty or all commented yaml, will get a null map
// this is not an error
if (config == null) {
config = new LinkedHashMap<>();
}
// 将加载的数据和路径封装为ConfigModel对象
ConfigModel configModel = new ConfigModel();
configModel.setUrl(url);
configModel.setConfig(config);
// ORDER_KEY的值为servicecomb-config-order
Object objOrder = config.get(ORDER_KEY);
if (objOrder == null) {
// compatible check 未来不再支持,如果配置了打印报错信息
objOrder = config.get("cse-config-order");
if (objOrder != null) {
LOGGER.error("cse-config-order will not be supported in future, please change it to servicecomb-config-order");
}
}
if (objOrder != null) {
if (Integer.class.isInstance(objOrder)) {
configModel.setOrder((int) objOrder);
} else {
configModel.setOrder(Integer.parseInt(String.valueOf(objOrder)));
}
}
return configModel;
}
protected abstract Map<String, Object> loadData(URL url) throws IOException;
// 从当前jar包中扫描microservice.yaml文件
protected List<URL> findURLFromClassPath(String resourceName) throws IOException {
List<URL> urlList = new ArrayList<>();
ClassLoader loader = JvmUtils.findClassLoader();
Enumeration<URL> urls = loader.getResources(resourceName);
while (urls.hasMoreElements()) {
urlList.add(urls.nextElement());
}
// 当前本地启动包含两个microservice.yaml文件
// file:/D:/WorkProject/servicecomb-java-chassis/samples/auth-sample/auth-provider/target/classes/microservice.yaml
// file:/D:/WorkProject/servicecomb-java-chassis/core/target/classes/microservice.yaml
return urlList;
}
private class ConfigModelWrapper {
ConfigModel model;
int addOrder;
}
// sort rule:
// 1.files in jar
// 2.smaller order
// 3.add to list earlier
protected void sort() {
List<ConfigModelWrapper> list = new ArrayList<>(configModels.size());
for (int idx = 0; idx < configModels.size(); idx++) {
ConfigModelWrapper wrapper = new ConfigModelWrapper();
wrapper.model = configModels.get(idx);
wrapper.addOrder = idx;
list.add(wrapper);
}
list.sort(this::doSort);
for (int idx = 0; idx < configModels.size(); idx++) {
configModels.set(idx, list.get(idx).model);
}
}
private int doSort(ConfigModelWrapper w1, ConfigModelWrapper w2) {
ConfigModel m1 = w1.model;
ConfigModel m2 = w2.model;
boolean isM1Jar = ResourceUtils.isJarURL(m1.getUrl());
boolean isM2Jar = ResourceUtils.isJarURL(m2.getUrl());
if (isM1Jar != isM2Jar) {
if (isM1Jar) {
return -1;
}
return 1;
}
// min order load first
int result = Integer.compare(m1.getOrder(), m2.getOrder());
if (result != 0) {
return result;
}
return doFinalSort(w1, w2);
}
private int doFinalSort(ConfigModelWrapper w1, ConfigModelWrapper w2) {
return Integer.compare(w1.addOrder, w2.addOrder);
}
}
Spring占位符的解析
org.apache.servicecomb.config.LastPropertyPlaceholderConfigurer进行spring占位符的解析:
// do not implements PriorityOrdered, must be Ordered
// because this bean must run after PropertyPlaceholderConfigurer
// this class's purpose: when asked to resolve placeholder, then throw exception directly
// 本方法的目的是让spring在解析占位符的时候,抛出异常,?,目前不理解
@Component
public class LastPropertyPlaceholderConfigurer implements BeanFactoryPostProcessor, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
new PropertyPlaceholderConfigurer().postProcessBeanFactory(beanFactory);
}
}
属性注入
注解
CSE提供了InjectProperties和InjectProperty注解。
InjectProperties注解的参数如下:
| 配置名称 | 默认值 | 说明 |
|---|---|---|
| prefix | "" | 绑定的配置的前缀 |
InjectProperty注解的参数如下:
| 配置名称 | 默认值 | 说明 |
|---|---|---|
| prefix | "" | 绑定的配置的前缀 |
| keys | {} | 配置的名称 |
| defaultValue | "" | 默认值 |
配置解析触发类
org.apache.servicecomb.config.priority.PriorityPropertyManager触发配置项的解析:
// 在SCBEngine类中新建PriorityPropertyManager对象
public class PriorityPropertyManager {
private ConfigurationListener configurationListener = this::configurationListener;
private Map<PriorityProperty<?>, PriorityProperty<?>> priorityPropertyMap = new ConcurrentHashMapEx<>();
private Map<Object, List<PriorityProperty<?>>> configObjectMap = new ConcurrentHashMapEx<>();
// will be reset to null after register or unregister
// and build when configuration changed
private Map<String, List<PriorityProperty<?>>> keyCache;
// 对应的构造方法
public PriorityPropertyManager() {
// make sure create a DynamicPropertyFactory instance
// otherwise will cause wrong order of configurationListeners
DynamicPropertyFactory.getInstance();
// 添加配置监听器
ConfigurationManager.getConfigInstance().addConfigurationListener(configurationListener);
}
public void close() {
ConfigurationManager.getConfigInstance().removeConfigurationListener(configurationListener);
}
// 使用此处的监听,当配置变化时,会触发对应的callback回调函数
public synchronized void configurationListener(ConfigurationEvent event) {
// 如果配置变化在更新前,直接退出
if (event.isBeforeUpdate()) {
return;
}
// 将更新配置的key放入keyCache中
if (keyCache == null) {
keyCache = new ConcurrentHashMapEx<>();
updateCache(priorityPropertyMap.values());
configObjectMap.values().stream().forEach(this::updateCache);
}
if (event.getPropertyName() != null) {
keyCache.getOrDefault(event.getPropertyName(), Collections.emptyList()).stream()
.forEach(p -> p.updateFinalValue(false));
return;
}
// event like add configuration source, need to make a global refresh
keyCache.values().stream().flatMap(Collection::stream).forEach(p -> p.updateFinalValue(false));
}
private void updateCache(Collection<PriorityProperty<?>> properties) {
for (PriorityProperty<?> priorityProperty : properties) {
for (String key : priorityProperty.getPriorityKeys()) {
keyCache.computeIfAbsent(key, k -> new ArrayList<>()).add(priorityProperty);
}
priorityProperty.updateFinalValue(false);
}
}
public Map<PriorityProperty<?>, PriorityProperty<?>> getPriorityPropertyMap() {
return priorityPropertyMap;
}
public Map<Object, List<PriorityProperty<?>>> getConfigObjectMap() {
return configObjectMap;
}
private synchronized void registerPriorityProperty(PriorityProperty<?> property) {
priorityPropertyMap.put(property, property);
keyCache = null;
}
private synchronized void registerConfigObject(Object configObject, List<PriorityProperty<?>> properties) {
// configObject类型为OperationConfig,properties为解析出的对象与值
configObjectMap.put(configObject, properties);
keyCache = null;
}
public synchronized void unregisterPriorityProperty(PriorityProperty<?> property) {
priorityPropertyMap.remove(property);
keyCache = null;
}
public synchronized void unregisterConfigObject(Object configObject) {
if (configObject == null) {
return;
}
configObjectMap.remove(configObject);
keyCache = null;
}
// OperationMeta的this.config = SCBEngine.getInstance().getPriorityPropertyManager().createConfigObject语句会调用的此处
// cls对应的为org.apache.servicecomb.core.definition.OperationConfig
// kvs中的值为["op-any-priority",["${schema}.${operation}","${schema}"],"consumer-op-any_priority",["${service}.${schema}.${operation}","${service}.${schema}","${service}"],"producer-op-any_priority",{"$ref":"$[1]"},"op-priority",[".${schema}.${operation}",".${schema}",""],"consumer-op-priority",[".${service}.${schema}.${operation}",".${service}.${schema}",".${service}",""],"producer-op-priority",{"$ref":"$[7]"},"consumer-producer","Provider","consumer-provider","Provider","service","auth-provider","schema","RestProviderImpl","operation","delete"]
public <T> T createConfigObject(Class<T> cls, Object... kvs) {
Map<String, Object> parameters = new HashMap<>();
// 将kvs数组转换为parameters Map对象
for (int idx = 0; idx < kvs.length; idx += 2) {
parameters.put(kvs[idx].toString(), kvs[idx + 1]);
}
//
return createConfigObject(cls, parameters);
}
public <T> T createConfigObject(Class<T> cls, Map<String, Object> parameters) {
// 新建ConfigObjectFactory对象
ConfigObjectFactory factory = new ConfigObjectFactory();
// 新建对应的OperationConfig对象
T configObject = factory.create(this, cls, parameters);
// 将对象存入到configObjectMap中缓存起来
registerConfigObject(configObject, factory.getPriorityProperties());
return configObject;
}
// 对于调用priorityPropertyManager.newPriorityProperty(Boolean.class, null, defaultValue, keys);:
// cls为java.lang.Boolean,invalidValue为null,defaultValue为false
// priorityKeys为["servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl.delete","servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl","servicecomb.metrics.Provider.invocation.slow.enabled","servicecomb.Provider.invocation.slow.enabled.RestProviderImpl.delete","servicecomb.Provider.invocation.slow.enabled.RestProviderImpl","servicecomb.Provider.invocation.slow.enabled"]
public <T> PriorityProperty<T> newPriorityProperty(Type cls, T invalidValue, T defaultValue,
String... priorityKeys) {
return new PriorityProperty<>(cls, invalidValue, defaultValue, priorityKeys);
}
public <T> PriorityProperty<T> createPriorityProperty(Type cls, T invalidValue, T defaultValue,
String... priorityKeys) {
PriorityProperty<T> priorityProperty = new PriorityProperty<>(cls, invalidValue, defaultValue, priorityKeys);
registerPriorityProperty(priorityProperty);
return priorityProperty;
}
}
org.apache.servicecomb.config.priority.PriorityProperty配置信息类:
public class PriorityProperty<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(PriorityProperty.class);
// priorityKeys[0] has the highest priority
private final String[] priorityKeys;
private final String joinedPriorityKeys;
// when got invalid value will try next level
// null always be a invalid value
private final T invalidValue;
// when got invalid value by lowest level, will use defaultValue
private final T defaultValue;
private DynamicProperty[] properties;
private T finalValue;
private Function<DynamicProperty, T> internalValueReader;
private Consumer<T> callback = v -> {
};
// 对于boolean类型:
// type值为java.lang.Boolean,invalidValue为null,defaultValue为false
// priorityKeys为["servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl.delete","servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl","servicecomb.metrics.Provider.invocation.slow.enabled","servicecomb.Provider.invocation.slow.enabled.RestProviderImpl.delete","servicecomb.Provider.invocation.slow.enabled.RestProviderImpl","servicecomb.Provider.invocation.slow.enabled"]
@SuppressWarnings("unchecked")
PriorityProperty(Type type, T invalidValue, T defaultValue, String... priorityKeys) {
//
internalValueReader = collectReader(type);
this.priorityKeys = priorityKeys;
this.joinedPriorityKeys = Arrays.toString(priorityKeys);
this.invalidValue = invalidValue;
this.defaultValue = defaultValue;
// 对应的key封装为archives的DynamicProperty对象
properties = new DynamicProperty[priorityKeys.length];
for (int idx = 0; idx < priorityKeys.length; idx++) {
String key = priorityKeys[idx].trim();
properties[idx] = DynamicProperty.getInstance(key);
}
updateFinalValue(true);
}
private Function<DynamicProperty, T> collectReader(Type type) {
if (type == int.class || type == Integer.class) {
return this::readInt;
}
if (type == long.class || type == Long.class) {
return this::readLong;
}
if (type == String.class) {
return this::readString;
}
if (type == boolean.class || type == Boolean.class) {
return this::readBoolean;
}
if (type == double.class || type == Double.class) {
return this::readDouble;
}
if (type == float.class || type == Float.class) {
return this::readFloat;
}
throw new IllegalStateException("not support, type=" + type.getTypeName());
}
@SuppressWarnings("unchecked")
protected T readInt(DynamicProperty property) {
return (T) property.getInteger();
}
@SuppressWarnings("unchecked")
protected T readLong(DynamicProperty property) {
return (T) property.getLong();
}
@SuppressWarnings("unchecked")
protected T readString(DynamicProperty property) {
return (T) property.getString();
}
@SuppressWarnings("unchecked")
protected T readBoolean(DynamicProperty property) {
return (T) property.getBoolean();
}
@SuppressWarnings("unchecked")
protected T readDouble(DynamicProperty property) {
return (T) property.getDouble();
}
@SuppressWarnings("unchecked")
protected T readFloat(DynamicProperty property) {
return (T) property.getFloat();
}
public String[] getPriorityKeys() {
return priorityKeys;
}
public T getDefaultValue() {
return defaultValue;
}
public DynamicProperty[] getProperties() {
return properties;
}
synchronized void updateFinalValue(boolean init) {
T lastValue = finalValue;
String effectiveKey = "default value";
T value = defaultValue;
for (DynamicProperty property : properties) {
T propValue = internalValueReader.apply(property);
if (propValue == null || propValue.equals(invalidValue)) {
continue;
}
effectiveKey = property.getName();
value = propValue;
break;
}
if (Objects.equals(lastValue, value)) {
return;
}
if (init) {
LOGGER.debug("config inited, \"{}\" set to {}, effective key is \"{}\".",
joinedPriorityKeys, value, effectiveKey);
} else {
LOGGER.info("config changed, \"{}\" changed from {} to {}, effective key is \"{}\".",
joinedPriorityKeys, finalValue, value, effectiveKey);
}
finalValue = value;
callback.accept(finalValue);
}
public T getValue() {
return finalValue;
}
public void setCallback(Consumer<T> callback) {
this.callback = callback;
callback.accept(finalValue);
}
}
org.apache.servicecomb.config.inject.ConfigObjectFactory的实现如下:
ConfigObjectFactory会进行注解的解析,解析InjectProperty和InjectProperties注解的信息。
/**
* must create by PriorityPropertyManager<br>
* or register to PriorityPropertyManager manually<br>
* <br>
* ${} or ${not-exist-key} is valid key in archaius<br>
* so this wrapper mechanism will not throw exception even can not find value by placeholder
*/
public class ConfigObjectFactory {
private PriorityPropertyManager priorityPropertyManager;
private Class<?> cls;
private Map<String, Object> parameters;
private Object instance;
private String prefix = "";
private List<PriorityProperty<?>> priorityProperties = new ArrayList<>();
// priorityPropertyManager为传入的调用类的信息
// cls为org.apache.servicecomb.core.definition.OperationConfig类
// parameters为对应的传入参数,对应上一层的调用入参
@SuppressWarnings("unchecked")
public <T> T create(PriorityPropertyManager priorityPropertyManager, Class<T> cls, Map<String, Object> parameters) {
this.priorityPropertyManager = priorityPropertyManager;
this.cls = cls;
this.parameters = parameters;
try {
// 新建对应的OperationConfig cls对象
instance = cls.newInstance();
} catch (Throwable e) {
throw new IllegalStateException("create config object failed, class=" + cls.getName(), e);
}
// 解析配置前置头,为servicecomb.
initPrefix();
doCreate();
return (T) instance;
}
public List<PriorityProperty<?>> getPriorityProperties() {
return priorityProperties;
}
// 解析OperationConfig中的InjectProperties注解信息
private void initPrefix() {
InjectProperties injectProperties = cls.getAnnotation(InjectProperties.class);
if (injectProperties == null) {
return;
}
// 类上使用的InjectProperties注解中的prefix的值为servicecomb
String prefix = injectProperties.prefix();
if (!prefix.isEmpty()) {
this.prefix = prefix + ".";
}
}
private void doCreate() {
// 使用jackson解析OperationConfig类对象中的字段信息
JavaType javaType = TypeFactory.defaultInstance().constructType(cls);
BeanDescription beanDescription = JsonUtils.OBJ_MAPPER.getSerializationConfig().introspect(javaType);
// beanDescription.findProperties()返回每一个字段的描述信息
// propertyDefinition的类型为POJOPropertyBuilder
for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) {
if (propertyDefinition.getField() == null) {
continue;
}
// 字段有对应的setter方法,并且字段不是public
if (propertyDefinition.getSetter() == null && !propertyDefinition.getField().isPublic()) {
continue;
}
// 将所有的字段逐一封装为Setter接口的形式
Setter<Object, Object> setter = propertyDefinition.getSetter() == null ?
LambdaMetafactoryUtils.createSetter(propertyDefinition.getField().getAnnotated()) :
LambdaMetafactoryUtils.createLambda(propertyDefinition.getSetter().getAnnotated(), Setter.class);
PriorityProperty<?> priorityProperty = createPriorityProperty(propertyDefinition.getField().getAnnotated());
// 此处,增加回调方法,在配置值改变时,调用instance的set方法,将对应的值存入对应的值
priorityProperty.setCallback(value -> setter.set(instance, value));
priorityProperties.add(priorityProperty);
}
}
// 创建PriorityProperty对象
private PriorityProperty<?> createPriorityProperty(Field field) {
// 解析对应的InjectProperty注解,获取其中配置的key对应的配置信息
String[] keys = collectPropertyKeys(field);
// 对于slowInvocationEnabled字段其类型为boolean
Class<?> fieldCls = field.getType();
switch (fieldCls.getName()) {
case "int":
return createIntProperty(field, keys, 0);
case "java.lang.Integer":
return createIntProperty(field, keys, null);
case "long":
return createLongProperty(field, keys, 0L);
case "java.lang.Long":
return createLongProperty(field, keys, null);
case "java.lang.String":
return createStringProperty(field, keys);
case "float":
return createFloatProperty(field, keys, 0f);
case "java.lang.Float":
return createFloatProperty(field, keys, null);
case "double":
return createDoubleProperty(field, keys, 0.0);
case "java.lang.Double":
return createDoubleProperty(field, keys, null);
case "boolean":
return createBooleanProperty(field, keys, false);
case "java.lang.Boolean":
return createBooleanProperty(field, keys, null);
}
throw new IllegalStateException("not support, field=" + field);
}
private PriorityProperty<?> createStringProperty(Field field, String[] keys) {
String defaultValue = null;
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
defaultValue = injectProperty.defaultValue();
}
}
return priorityPropertyManager.newPriorityProperty(String.class, null, defaultValue, keys);
}
private PriorityProperty<?> createDoubleProperty(Field field, String[] keys, Double defaultValue) {
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
defaultValue = Double.parseDouble(injectProperty.defaultValue());
}
}
return priorityPropertyManager.newPriorityProperty(Double.class, null, defaultValue, keys);
}
private PriorityProperty<?> createFloatProperty(Field field, String[] keys, Float defaultValue) {
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
defaultValue = Float.parseFloat(injectProperty.defaultValue());
}
}
return priorityPropertyManager.newPriorityProperty(Float.class, null, defaultValue, keys);
}
// 将字段信息封装为PriorityProperty对象,利用archives的配置管理能力
private PriorityProperty<?> createBooleanProperty(Field field, String[] keys, Boolean defaultValue) {
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
// 对于slowInvocationEnabled字段,注解存在
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
// defaultValue配置为false
defaultValue = Boolean.parseBoolean(injectProperty.defaultValue());
}
}
return priorityPropertyManager.newPriorityProperty(Boolean.class, null, defaultValue, keys);
}
private PriorityProperty<?> createLongProperty(Field field, String[] keys, Long defaultValue) {
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
defaultValue = Long.parseLong(injectProperty.defaultValue());
}
}
return priorityPropertyManager.newPriorityProperty(Long.class, null, defaultValue, keys);
}
private PriorityProperty<?> createIntProperty(Field field, String[] keys, Integer defaultValue) {
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.defaultValue().isEmpty()) {
defaultValue = Integer.parseInt(injectProperty.defaultValue());
}
}
return priorityPropertyManager.newPriorityProperty(Integer.class, null, defaultValue, keys);
}
// 获取配置的key
private String[] collectPropertyKeys(Field field) {
// 开头为servicecomb.
String propertyPrefix = prefix;
// 字段的名称,此处以slowInvocationEnabled为例进行分析
String[] keys = new String[] {field.getName()};
// 获取字段的注解
// 注解的信息为 prefix为空,defaultValue为false,keys为["metrics.${consumer-producer}.invocation.slow.enabled${op-priority}", "${consumer-producer}.invocation.slow.enabled${op-priority}"]
InjectProperty injectProperty = field.getAnnotation(InjectProperty.class);
if (injectProperty != null) {
if (!injectProperty.prefix().isEmpty()) {
propertyPrefix = injectProperty.prefix() + ".";
}
if (injectProperty.keys().length != 0) {
// 获取对应的配置的keys参数,keys中为对应的配置项的名称
keys = injectProperty.keys();
}
}
List<String> finalKeys = new ArrayList<>();
// 一一解析每一个配置项的值
// 此处的parameters为this.config = SCBEngine.getInstance().getPriorityPropertyManager().createConfigObject传入的参数
// 举例: {"consumer-op-priority":[".${service}.${schema}.${operation}",".${service}.${schema}",".${service}",""],"schema":"RestProviderImpl","consumer-op-any_priority":["${service}.${schema}.${operation}","${service}.${schema}","${service}"],"consumer-provider":"Provider","producer-op-any_priority":["${schema}.${operation}","${schema}"],"service":"auth-provider","producer-op-priority":[".${schema}.${operation}",".${schema}",""],"consumer-producer":"Provider","op-priority":[".${schema}.${operation}",".${schema}",""],"operation":"delete","op-any-priority":["${schema}.${operation}","${schema}"]}
// key为 servicecomb.metrics.${consumer-producer}.invocation.slow.enabled${op-priority}
// 解析的结果为: ["servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl.delete","servicecomb.metrics.Provider.invocation.slow.enabled.RestProviderImpl","servicecomb.metrics.Provider.invocation.slow.enabled"]
for (String key : keys) {
List<String> resolvedKeys = new PlaceholderResolver().replace(propertyPrefix + key, parameters);
finalKeys.addAll(resolvedKeys);
}
// 此处将InjectProperty注解的key中的占位符进行解析
return finalKeys.toArray(new String[finalKeys.size()]);
}
}
org.apache.servicecomb.core.definition.OperationConfig的实现如下:
OperationConfig定义了很多的配置注入字段,定义了CSE服务的配置参数。
// InjectProperties设置配置的头均为servicecomb
@InjectProperties(prefix = "servicecomb")
public class OperationConfig {
public static final List<String> CONSUMER_OP_ANY_PRIORITY = Arrays.asList(
"${service}.${schema}.${operation}",
"${service}.${schema}",
"${service}");
public static final List<String> PRODUCER_OP_ANY_PRIORITY = Arrays.asList(
"${schema}.${operation}",
"${schema}");
public static final List<String> CONSUMER_OP_PRIORITY = Arrays.asList(
".${service}.${schema}.${operation}",
".${service}.${schema}",
".${service}",
"");
public static final List<String> PRODUCER_OP_PRIORITY = Arrays.asList(
".${schema}.${operation}",
".${schema}",
"");
@InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.enabled${op-priority}",
"${consumer-producer}.invocation.slow.enabled${op-priority}"}, defaultValue = "false")
private boolean slowInvocationEnabled;
@InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.msTime${op-priority}",
"${consumer-producer}.invocation.slow.msTime${op-priority}"}, defaultValue = "1000")
private long msSlowInvocation;
private long nanoSlowInvocation;
/**
* consumer request timeout
*/
@InjectProperty(keys = {"request.${op-any-priority}.timeout", "request.timeout"}, defaultValue = "30000")
private long msRequestTimeout;
/**
* whether to remove certain headers from the 3rd party invocations
*/
@InjectProperty(keys = {"request.clientRequestHeaderFilterEnabled${consumer-op-priority}"}, defaultValue = "true")
private boolean clientRequestHeaderFilterEnabled = true;
/**
* producer wait in thread pool timeout
*/
@InjectProperty(keys = {
"Provider.requestWaitInPoolTimeout${op-priority}",
"highway.server.requestWaitInPoolTimeout"}, defaultValue = "30000")
private long msHighwayRequestWaitInPoolTimeout;
private long nanoHighwayRequestWaitInPoolTimeout;
@InjectProperty(keys = {
"Provider.requestWaitInPoolTimeout${op-priority}",
"rest.server.requestWaitInPoolTimeout"}, defaultValue = "30000")
private long msRestRequestWaitInPoolTimeout;
private long nanoRestRequestWaitInPoolTimeout;
public boolean isSlowInvocationEnabled() {
return slowInvocationEnabled;
}
public void setSlowInvocationEnabled(boolean slowInvocationEnabled) {
this.slowInvocationEnabled = slowInvocationEnabled;
}
public long getMsSlowInvocation() {
return msSlowInvocation;
}
public void setMsSlowInvocation(long msSlowInvocation) {
this.msSlowInvocation = msSlowInvocation;
this.nanoSlowInvocation = TimeUnit.MILLISECONDS.toNanos(msSlowInvocation);
}
public long getNanoSlowInvocation() {
return nanoSlowInvocation;
}
public long getMsRequestTimeout() {
return msRequestTimeout;
}
public void setMsRequestTimeout(long msRequestTimeout) {
this.msRequestTimeout = msRequestTimeout;
}
public long getMsHighwayRequestWaitInPoolTimeout() {
return msHighwayRequestWaitInPoolTimeout;
}
public void setMsHighwayRequestWaitInPoolTimeout(long msHighwayRequestWaitInPoolTimeout) {
this.msHighwayRequestWaitInPoolTimeout = msHighwayRequestWaitInPoolTimeout;
this.nanoHighwayRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msHighwayRequestWaitInPoolTimeout);
}
public long getNanoHighwayRequestWaitInPoolTimeout() {
return nanoHighwayRequestWaitInPoolTimeout;
}
public long getMsRestRequestWaitInPoolTimeout() {
return msRestRequestWaitInPoolTimeout;
}
public void setMsRestRequestWaitInPoolTimeout(long msRestRequestWaitInPoolTimeout) {
this.msRestRequestWaitInPoolTimeout = msRestRequestWaitInPoolTimeout;
this.nanoRestRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msRestRequestWaitInPoolTimeout);
}
public long getNanoRestRequestWaitInPoolTimeout() {
return nanoRestRequestWaitInPoolTimeout;
}
public boolean isClientRequestHeaderFilterEnabled() {
return clientRequestHeaderFilterEnabled;
}
public void setClientRequestHeaderFilterEnabled(boolean clientRequestHeaderFilterEnabled) {
this.clientRequestHeaderFilterEnabled = clientRequestHeaderFilterEnabled;
}
}
org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils创建通过反射设置类字段的方法:
public final class LambdaMetafactoryUtils {
private static Field allowedModesField;
private static final int ALL_MODES = (MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC);
private static final Lookup LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>, Class<?>> GETTER_MAP = new HashMap<>();
private static final Map<Class<?>, Class<?>> SETTER_MAP = new HashMap<>();
static {
enhanceLambda();
initGetterSetterMap();
}
private static void initGetterSetterMap() {
GETTER_MAP.put(boolean.class, BoolGetter.class);
GETTER_MAP.put(byte.class, ByteGetter.class);
GETTER_MAP.put(short.class, ShortGetter.class);
GETTER_MAP.put(int.class, IntGetter.class);
GETTER_MAP.put(long.class, LongGetter.class);
GETTER_MAP.put(float.class, FloatGetter.class);
GETTER_MAP.put(double.class, DoubleGetter.class);
SETTER_MAP.put(boolean.class, BoolSetter.class);
SETTER_MAP.put(byte.class, ByteSetter.class);
SETTER_MAP.put(short.class, ShortSetter.class);
SETTER_MAP.put(int.class, IntSetter.class);
SETTER_MAP.put(long.class, LongSetter.class);
SETTER_MAP.put(float.class, FloatSetter.class);
SETTER_MAP.put(double.class, DoubleSetter.class);
}
private static void enhanceLambda() {
try {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
allowedModesField = Lookup.class.getDeclaredField("allowedModes");
allowedModesField.setAccessible(true);
int modifiers = allowedModesField.getModifiers();
modifiersField.setInt(allowedModesField, modifiers & ~Modifier.FINAL);
} catch (Throwable e) {
throw new IllegalStateException("Failed to init LambdaMetafactoryUtils.", e);
}
}
private LambdaMetafactoryUtils() {
}
// 找到对应的abstract method方法
// functionalInterface对应的是Setter.class对象
// public interface Setter<C, F> {
// void set(C instance, F value);
//}
protected static Method findAbstractMethod(Class<?> functionalInterface) {
for (Method method : functionalInterface.getMethods()) {
// 接口对应的方法为abstract方法
if ((method.getModifiers() & Modifier.ABSTRACT) != 0) {
return method;
}
}
return null;
}
@SuppressWarnings("unchecked")
public static <T> T createLambda(Object instance, Method instanceMethod, Class<?> functionalIntfCls) {
try {
Lookup lookup = LOOKUP.in(instanceMethod.getDeclaringClass());
allowedModesField.set(lookup, ALL_MODES);
Method intfMethod = findAbstractMethod(functionalIntfCls);
MethodHandle methodHandle = lookup.unreflect(instanceMethod);
MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes());
MethodType instanceMethodType = MethodType
.methodType(instanceMethod.getReturnType(), instanceMethod.getParameterTypes());
CallSite callSite = LambdaMetafactory.metafactory(
lookup,
intfMethod.getName(),
MethodType.methodType(functionalIntfCls, instance.getClass()),
intfMethodType,
methodHandle,
instanceMethodType);
return (T) callSite.getTarget().bindTo(instance).invoke();
} catch (Throwable e) {
throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e);
}
}
// instanceMethod对应的方法为public void org.apache.servicecomb.core.definition.OperationConfig.setSlowInvocationEnabled(boolean)
// invokedynamic 是 Java 7 引入的一条新指令,用以支持动态语言的方法调用。具体来说,它将调用点(CallSite)抽象成一个 Java 类,并且将原本由 Java 虚拟机控制的方法调用以及方法链接暴露给了应用程序。在运行过程中,每一条 invokedynamic 指令将**一个调用点,并且会调用该调用点所链接的方法句柄。
// createLambda实现将目标方法包装为对应的接口方法的功能
@SuppressWarnings("unchecked")
public static <T> T createLambda(Method instanceMethod, Class<?> functionalIntfCls) {
try {
// 找到对应的Lookup对象,设置可以访问所有的字段
Lookup lookup = LOOKUP.in(instanceMethod.getDeclaringClass());
allowedModesField.set(lookup, ALL_MODES);
// 找到functionalIntfCls中对应的set方法
Method intfMethod = findAbstractMethod(functionalIntfCls);
// 使用MethodHandlers.Lookup.unreflect可以构建对应方法的代理
MethodHandle methodHandle = lookup.unreflect(instanceMethod);
// java.lang.invoke中的MethodType封装了方法的参数
// intfMethodType的值为(Object,Object)void,
MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes());
// instanceMethodType的值为(OperationConfig,boolean)void
MethodType instanceMethodType = methodHandle.type();
// lookup对应instanceMethod的声明类信息
// intfMethod.getName()为接口方法的名称,如果为IntGetter接口,对应的名称为get
// MethodType.methodType(functionalIntfCls) 期望的方法的调用类型
// intfMethodType需要实现的方法的入参和出参的类型信息
// methodHandle需要实现的方法
// instanceMethodType需要实现的方法的参数信息
CallSite callSite = LambdaMetafactory.metafactory(
lookup,
intfMethod.getName(),
MethodType.methodType(functionalIntfCls),
intfMethodType,
methodHandle,
instanceMethodType);
// 通过CallSite新建对应的functionalIntfCls对象
return (T) callSite.getTarget().invoke();
} catch (Throwable e) {
throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e);
}
}
public static <T> T createGetter(Method getMethod) {
// 查询本地缓存中是否存在该方法对应的信息
Class<?> getterCls = GETTER_MAP.getOrDefault(getMethod.getReturnType(), Getter.class);
// getterCls对应的为interface org.apache.servicecomb.foundation.common.utils.bean.IntGetter对象
return createLambda(getMethod, getterCls);
}
// slower than reflect directly
@SuppressWarnings("unchecked")
public static <C, F> Getter<C, F> createGetter(Field field) {
field.setAccessible(true);
return instance -> {
try {
return (F) field.get(instance);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
public static <T> T createSetter(Method setMethod) throws Throwable {
Class<?> setterCls = SETTER_MAP.getOrDefault(setMethod.getParameterTypes()[0], Setter.class);
return createLambda(setMethod, setterCls);
}
// slower than reflect directly
// 如果字段没有对应的setter方法,通过反射设置字段是可访问的,并返回设置字段的方法
public static <C, F> Setter<C, F> createSetter(Field field) {
field.setAccessible(true);
return (instance, value) -> {
try {
field.set(instance, value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
}
此处以单元测试用例方法,分析接口方法的代理新建的逻辑:
@Test
public void createGetterSetterByMethod() throws Throwable {
// Model.class.getMethod("getF1")新建对应的getF1对应的Method
IntGetter<Model> getter = LambdaMetafactoryUtils.createGetter(Model.class.getMethod("getF1"));
IntSetter<Model> setter = LambdaMetafactoryUtils.createSetter(Model.class.getMethod("setF1", int.class));
BiFunction<Object, Object, Object> echo = LambdaMetafactoryUtils
.createLambda(Model.class.getMethod("echo", List.class), BiFunction.class);
setter.set(model, 1);
int f1 = getter.get(model);
Assert.assertEquals(1, f1);
Assert.assertThat((List<Integer>) echo.apply(model, Arrays.asList(2)), Matchers.contains(2));
}
org.apache.servicecomb.config.inject.PlaceholderResolver实现了解析占位符参数的功能:
/**
* <pre>
* not care for performance
*
* behavior of multiple list if defined:
* org.apache.servicecomb.config.inject.TestPlaceholderResolver#multi_list()
* behavior of nested and multiple list variable is undefined
* org.apache.servicecomb.config.inject.TestPlaceholderResolver#mixed()
* </pre>
*/
public class PlaceholderResolver {
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(?<escape>\\\\)?\\$\\{(?<name>[^{}]+)\\}");
// 静态内部类,存储输入字符串的每段的解析结果
static class SplitPart {
// 是否是变量标记
boolean var;
// 全名称
String fullName;
// 对应的值
Object value;
public SplitPart(boolean var, String fullName) {
this.var = var;
this.fullName = fullName;
}
@Override
public String toString() {
return "SplitPart{" +
"var=" + var +
", fullName='" + fullName + '\'' +
", value=" + value +
'}';
}
}
// 静态内部类,封装最终的key的解析结果
static class Row {
List<SplitPart> parts = new ArrayList<>();
int cartesianProductCount = 1;
int varCount = 0;
}
// 例如 str为prefix.${key}.suffix
// parameters为
// parameters.put("key", "value");
// parameters.put("varOfVar", Arrays.asList("${key}"));
// parameters.put("priority", "low");
// parameters.put("low-list", Arrays.asList("low-1", "low-2"));
// parameters.put("middle-list", Arrays.asList("middle-1", "middle-2"));
// parameters.put("high-list", Arrays.asList("high-1", "high-2"));
public List<String> replace(String str, Map<String, Object> parameters) {
// 获取到最值的解析结果,其中每个结果中,varCount均为0
List<Row> finalRows = replaceToRows(str, parameters);
List<String> replaced = new ArrayList<>();
for (Row row : finalRows) {
// 从finalRows中获取对应的结果,存入replaced中
resolve(row, replaced);
}
for (int idx = 0; idx < replaced.size(); idx++) {
String row = replaced.get(idx);
// 过滤其中包含的\\$替换为$符号
replaced.set(idx, row.replace("\\$", "$"));
}
return replaced;
}
private List<Row> replaceToRows(String str, Map<String, Object> parameters) {
List<Row> finalRows = new ArrayList<>();
List<String> remainRows = new ArrayList<>();
// finalRows为最终的解析结果,remainRows对应解析的中间结果
replaceToRows(str, parameters, remainRows, finalRows);
for (String row : remainRows) {
// 递归调用replaceToRows,获取最终的解析结果
List<Row> nestedRows = replaceToRows(row, parameters);
finalRows.addAll(nestedRows);
}
return finalRows;
}
private void replaceToRows(String str, Map<String, Object> parameters, List<String> remainRows,
List<Row> finalRows) {
// 将key解析为对应的Row对象
Row row = parseToRow(str, parameters);
if (row.varCount == 0 && row.cartesianProductCount == 1) {
finalRows.add(row);
return;
}
// 获取对应的解析结果
resolve(row, remainRows);
}
// str对应的是prefix.${key}.suffix
private Row parseToRow(String str, Map<String, Object> parameters) {
Matcher matcher = PLACEHOLDER_PATTERN.matcher(str);
Row row = new Row();
int last = 0;
// 使用正则表达式对待匹配对象进行一一匹配,寻找其中的${}占位符
while (matcher.find()) {
row.parts.add(new SplitPart(false, str.substring(last, matcher.start())));
last = matcher.end();
if (matcher.group("escape") != null) {
// 构建对应的SplitPart对象,找到escape的匹配对象
row.parts.add(new SplitPart(false, matcher.group().substring(1)));
continue;
}
// 找到name的匹配对象,对应值为key
String name = matcher.group("name");
Object value = findValue(parameters, name);
if (value instanceof Collection) {
// 如果解析的参数是List,则存在笛卡尔积的问题,会解析出多种结果出来
row.cartesianProductCount *= ((Collection) value).size();
}
if (value != null) {
row.varCount++;
}
// 将匹配结果封装为SplitPart对象
SplitPart splitPart = new SplitPart(value != null, matcher.group());
splitPart.value = value;
//
row.parts.add(splitPart);
}
row.parts.add(new SplitPart(false, str.substring(last)));
return row;
}
// resolve placeholder and execute cartesian product
// row为对应的key的解析结果
@SuppressWarnings("unchecked")
private void resolve(Row row, List<String> resolvedRows) {
// 笛卡尔积
List<StringBuilder> stringBuilders = new ArrayList<>();
for (int idx = 0; idx < row.cartesianProductCount; idx++) {
stringBuilders.add(new StringBuilder());
}
int collectionRepeatCount = 1;
// 对于key的解析出来的每一部分,组合为最终的结果
for (SplitPart part : row.parts) {
if (!part.var) {
for (int idx = 0; idx < row.cartesianProductCount; idx++) {
StringBuilder sb = stringBuilders.get(idx);
if (part.fullName.startsWith("$")) {
sb.append("\\" + part.fullName);
continue;
}
sb.append(part.fullName);
}
continue;
}
if (part.value instanceof Collection) {
int size = ((Collection<String>) part.value).size();
int rowRepeatCount = row.cartesianProductCount / size / collectionRepeatCount;
int valueIdx = 0;
for (int collectionRepeatIdx = 0; collectionRepeatIdx < collectionRepeatCount; collectionRepeatIdx++) {
for (String value : (Collection<String>) part.value) {
for (int repeatIdx = 0; repeatIdx < rowRepeatCount; repeatIdx++) {
StringBuilder sb = stringBuilders.get(valueIdx);
valueIdx++;
sb.append(value);
}
}
}
collectionRepeatCount *= size;
continue;
}
// normal var
for (int idx = 0; idx < row.cartesianProductCount; idx++) {
StringBuilder sb = stringBuilders.get(idx);
sb.append(part.value);
}
}
for (StringBuilder sb : stringBuilders) {
resolvedRows.add(sb.toString());
}
}
private Object findValue(Map<String, Object> parameters, String key) {
Object value = parameters.get(key);
if (value == null) {
// 如果传入的parameters找不到,通过ConfigUtil进行寻找
value = ConfigUtil.getProperty(key);
}
return value;
}
}
org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils的修改对应的this.config = SCBEngine.getInstance().getPriorityPropertyManager().createConfigObject设置的配置项的值:
public static void setProperty(String key, Object value) {
// ensure have instance
DynamicPropertyFactory.getInstance();
// 获取到对应的配置
ConcurrentCompositeConfiguration config = (ConcurrentCompositeConfiguration) DynamicPropertyFactory
.getBackingConfigurationSource();
if (value != null) {
// 选择一个数据源,将对应的值设置进入其中即可
config.getConfiguration(0).setProperty(key, value);
return;
}
config.getConfiguration(0).clearProperty(key);
}
NUWA的自定义配置加载逻辑
nuwa使用com.huawei.nuwa.config.ConfigurationService实现了自定义配置的加载逻辑:
public class ConfigurationService extends PropertyPlaceholderConfigurer implements DisposableBean {
private static final int NORMAL_FILE_COUNT = 2;
private static final int ORDER = Ordered.LOWEST_PRECEDENCE / 2 - 100;
private static final ConfigurationService INSTANCE = new ConfigurationService();
/**
* Gets instance.
*
* @return the instance
*/
public static ConfigurationService getInstance() {
return INSTANCE;
}
private final ConcurrentCompositeConfiguration compositeConfiguration;
private final ConvertableConfiguration convertableConfiguration;
private final TreeSet<ConfigurationLoader> configurationLoaders;
private ScheduledExecutorService scheduler;
private ConfigurationService() {
// !!保证比CSE的org.apache.servicecomb.config.ConfigurationSpringInitializer晚加载!!
setOrder(ORDER);
// 对于无法解析的占位符和无法发现的文件不报错
setIgnoreUnresolvablePlaceholders(true);
setIgnoreResourceNotFound(true);
// 去除配置值前后的空格
setTrimValues(true);
// 获取配置管理对象
compositeConfiguration = new ConcurrentCompositeConfiguration();
// 不使用分割符将string配置分割为list
compositeConfiguration.setDelimiterParsingDisabled(true);
// ConvertableConfiguration是对ConcurrentCompositeConfiguration的封装
convertableConfiguration = new ConvertableConfiguration(compositeConfiguration);
convertableConfiguration.setDelimiterParsingDisabled(true);
// nuwa提供了一个配置的加载扩展,通过SPI模式加载对应的实现类,对应的接口为ConfigurationLoader
// NuwaFactoriesLoader.loadFactories实现了对应的自定义配置类的加载功能
configurationLoaders = Sets.newTreeSet(
NuwaFactoriesLoader.loadFactories(ConfigurationLoader.class, ConfigurationService.class.getClassLoader(),
true));
// scheduler实现对于配置项的定时刷新功能
scheduler = Executors.newScheduledThreadPool(NORMAL_FILE_COUNT,
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("nuwa-monitor-file-%d").build());
this.compositeConfiguration.addConfiguration(InnerDefaultConfigurationHolder.getInstance().getConfiguration(),
"NuwaInnerDefault");
}
/**
* 初始化converters
* 在塞给com.netflix.config的时候要初始化先
*
* @see DynamicPropertyRegistry#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
*/
public void initPropertyConverters() {
Properties properties = new Properties();
Iterator<String> iter = compositeConfiguration.getKeys();
while (iter.hasNext()) {
String key = iter.next();
Object value = compositeConfiguration.getProperty(key);
properties.setProperty(key, value.toString());
}
PropertyConverters converters = PropertyConverters.getInstance();
converters.init(properties);
}
/**
* Merge properties properties.
*
* @return the properties
* @throws IOException the io exception
*/
@Override
protected Properties mergeProperties() throws IOException {
Properties properties = super.mergeProperties();
Iterator<String> iter = compositeConfiguration.getKeys();
while (iter.hasNext()) {
String key = iter.next();
Object value = compositeConfiguration.getProperty(key);
properties.setProperty(key, value.toString());
}
PropertyConverters converters = PropertyConverters.getInstance();
Properties newProperties = new Properties(properties);
properties.forEach((key1, oldValue) -> {
String key = (String) key1;
Object newValue = converters.convert(key, oldValue);
newProperties.put(key, newValue);
});
return newProperties;
}
/**
* Gets convertable configuration.
*
* @return the convertable configuration
*/
public ConcurrentCompositeConfiguration getConvertableConfiguration() {
return convertableConfiguration;
}
/**
* Gets configuration.
*
* @return the configuration
*/
public ConcurrentCompositeConfiguration getConfiguration() {
return compositeConfiguration;
}
/**
* Add configuration.
*
* @param configuration the configuration
*/
public void addConfiguration(AbstractConfiguration configuration) {
this.compositeConfiguration.addConfigurationAtFront(configuration, null);
}
/**
* Add configuration url.
*
* @param cfgURL the cfg url
*/
public void addConfigurationUrl(URL cfgURL) {
addConfiguration(createDynamicConfiguration(cfgURL));
}
private DynamicConfiguration createDynamicConfiguration(URL cfgURL) {
return createDynamicConfiguration(cfgURL, new FixedRatePollingScheduler(scheduler));
}
private DynamicConfiguration createDynamicConfiguration(URL cfgURL, AbstractPollingScheduler scheduler) {
Configuration configuration = createConfiguration(cfgURL);
DynamicConfiguration dynamicConfiguration = new DynamicConfiguration();
dynamicConfiguration.setDelimiterParsingDisabled(true); //
dynamicConfiguration.startPolling(new DelegatedConfigurationSource(configuration), scheduler);
return dynamicConfiguration;
}
private AbstractConfiguration createConfiguration(URL cfgURL) {
for (ConfigurationLoader loader : configurationLoaders) {
if (loader.accept(cfgURL)) {
try {
AbstractConfiguration configuration = loader.loadConfiguration(cfgURL);
if (configuration != null) {
return configuration;
}
} catch (ConfigurationException e) {
throw new IllegalArgumentException("cannot resolve the config file :" + cfgURL, e);
}
}
}
throw new IllegalArgumentException("no configuration loader to process the config file :" + cfgURL);
}
/**
* Destroy.
*/
@Override
public void destroy() {
if (scheduler != null) {
scheduler.shutdown();
}
}
private static class ConvertableConfiguration extends ConcurrentCompositeConfiguration {
/**
* Instantiates a new Convertable configuration.
*
* @param delegated the delegated
*/
public ConvertableConfiguration(AbstractConfiguration delegated) {
super();
addConfiguration(delegated);
}
/**
* Gets property.
*
* @param s the s
* @return the property
*/
@Override
public Object getProperty(String s) {
// 此处会进行一些解密操作
return PropertyConverters.getInstance().convert(s, super.getProperty(s));
}
/**
* Get string array string [ ].
*
* @param key the key
* @return the string [ ]
*/
public String[] getStringArray(String key) {
String[] tokens = super.getStringArray(key);
if (tokens != null && tokens.length > 0) {
return Arrays.stream(tokens)
.map(s -> PropertyConverters.getInstance().convert(key, s))
.toArray(String[]::new);
}
return tokens;
}
@Override
public void addConfigurationListener(ConfigurationListener listener) {
super.addConfigurationListener(event -> listener.configurationChanged(
new ConfigurationEvent(event.getSource(), event.getType(), event.getPropertyName(),
PropertyConverters.getInstance().convert(event.getPropertyName(), event.getPropertyValue()),
event.isBeforeUpdate())));
}
}
}
com.huawei.nuwa.ConfigInitializer实现了加载指定配置路径下的配置的功能:
/**
* 加载机器主机信息
* 加载classpath下配置文件
* 解决SDS\DCS的DC名称
*
* @author z00249298
* @since 2019-09-16
*/
class ConfigInitializer {
// 加载的配置项的路径
private static final String[] CONFIG_LOCATIONS = SystemHelper.getArrayProperty("nuwa.system.configLocations", ",",
new String[] {"classpath*:*config.properties", "classpath*:*config.yaml"});
private ConfigInitializer() {
}
/**
* Init.
*/
static void init() {
StartupInfoLogger.getLogger().info("Loaded configuration files from " + Arrays.toString(CONFIG_LOCATIONS));
// 加载类路径下的配置文件
loadClassPathConfig();
}
private static void loadClassPathConfig() {
if (ArrayUtils.isNotEmpty(CONFIG_LOCATIONS)) {
ConfigurationService configurationService = ConfigurationService.getInstance();
// 逐一加载对应配置路径下的配置文件的信息
for (String location : CONFIG_LOCATIONS) {
try {
// 通过 ResourceLoader获取对应的配置项的坐标
Resource[] resources = ResourceLoader.getResource(ConfigInitializer.class.getClassLoader(),
location);
if (ArrayUtils.isNotEmpty(resources)) {
for (Resource resource : resources) {
if (Optional.ofNullable(resource)
.map(Resource::getFilename)
.map(fileName -> fileName.startsWith("."))
.orElse(false)) {
continue;
}
// 对于获取到的所有配置文件加载到ConfigurationService中
configurationService.addConfigurationUrl(resource.getURL());
StartupInfoLogger.getLogger().info("Loaded configuration file url: " + resource.getURL());
}
}
} catch (IOException e) {
throw new NuwaRuntimeException("failed to load config resource", e);
}
}
}
}
}
Webplatform的配置加载逻辑
com.huawei.webplatform.propertyconfig.ConfigurationService类继承了PropertyPlaceholderConfigurer实现自定义的加载类:
webplatform配置加载类比nuwa的配置加载类后加载,通过init方法,将自定义的配置文件信息加载到archaius配置管理类中。
@Order(Integer.MIN_VALUE)
public class ConfigurationService extends PropertyPlaceholderConfigurer {
...
/**
* 初始化
*/
public void init() {
LOGGER.info("init {}", webplatformLocations.length);
if (ArrayUtils.isNotEmpty(webplatformLocations)) {
for (Resource resource : webplatformLocations) {
try {
addConfigurationUrl(resource.getURL());
} catch (IOException e) {
throw new ComRuntimeException("failed to load config resource", e);
}
}
}
}
...
}
总结
(1)ConfigurationSpringInitializer实现了配置文件的加载 (2)InjectProperty和InjectProperties实现了自定义配置的加载和更新能力