(前言:以provider消费者为例进行说明)
一. 配置参数解析入口
1.provider
配置参数解析入口:
ServiceBean
实现 ApplicationListener
(spring 资源解析)重写onApplicationEvent()
方法,提供暴露服务
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export(); //入口
}
}
二.xxx.properties缺省配置
@From:《dubbo文档-配置-属性配置》
如果公共配置很简单,没有多注册中心,多协议等情况,或者想多个 Spring 容器想共享配置,可以使用 dubbo.properties 作为缺省配置。
Dubbo 将自动加载 classpath 根目录下的 dubbo.properties,可以通过JVM启动参数 -Ddubbo.properties.file=xxx.properties 改变缺省配置位置。[1]
测试缺省配置:
org.apache.dubbo.config.AbstractInterfaceConfig#checkApplication#checkApplication() 获取属性信息
// org.apache.dubbo.config.AbstractInterfaceConfig#checkApplication#checkApplication()
protected void checkApplication() {
// for backward compatibility
if (application == null) {
String applicationName = ConfigUtils.getProperty("dubbo.application.name");//提取配置信息 @sjt 1
if (applicationName != null && applicationName.length() > 0) {
application = new ApplicationConfig();
}
}
@sjt1: 获取
dubbo.application.name
的配置信息
org.apache.dubbo.common.utils.ConfigUtils#getProperties()获取jvm文件
// org.apache.dubbo.common.utils.ConfigUtils#getProperties()
public static Properties getProperties() {
if (PROPERTIES == null) {
synchronized (ConfigUtils.class) {
if (PROPERTIES == null) {
String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY); //DUBBO_PROPERTIES_KEY = "dubbo.properties.file"
if (path == null || path.length() == 0) {
path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);
if (path == null || path.length() == 0) {
path = Constants.DEFAULT_DUBBO_PROPERTIES; //加载默认文件 DEFAULT_DUBBO_PROPERTIES = "dubbo.properties"
}
}
PROPERTIES = ConfigUtils.loadProperties(path, false, true);
}
}
}
return PROPERTIES;
}
三.映射规则
属性配置和xml配置的映射规则。就是参数配置的不同方式
eg:
dubbo.application.name=foo
等价于 <dubbo:application name="foo" />
四.覆盖策略
jvm启动 > xml-配置 > dubbo.properties
- JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口
- XML 次之,如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。
- Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置(全局配置),比如应用名。
测试:覆盖策略:
准备参数:
- jvm:-Ddubbo.protocol.port=20880 -Ddubbo.protocol.id=jvm-id
- xml:<dubbo:protocol port=“20881” id=“xml-id” />
- dubbo.properties:dubbo.protocol.port=20882 dubbo.protocol.id=properties-name
准备参数:
jvm:-Ddubbo.protocol.port=20880 -Ddubbo.protocol.id=jvm-id- xml:<dubbo:protocol port=“20881” id=“xml-id” />
- dubbo.properties:dubbo.protocol.port=20882 dubbo.protocol.id=properties-name
准备参数:
jvm:-Ddubbo.protocol.port=20880 -Ddubbo.protocol.id=jvm-idxml:<dubbo:protocol port=“20881” id=“xml-id” />- dubbo.properties:dubbo.protocol.port=20882 dubbo.protocol.id=properties-name
源码解读:org.apache.dubbo.config.AbstractConfig#appendProperties()
/**
* @param config 配置对象
* @Description: 读取启动参数变量和 properties 配置到配置对象。
*/
protected static void appendProperties(AbstractConfig config) {
//。。。省略
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())// 方法是 public 的 setting 方法。
&& method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {// 方法的唯一参数是基本数据类型
// 获得属性名,例如 `ApplicationConfig#setName(...)` 方法,对应的属性名为 name 。
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), ".");
// 【启动参数变量】优先从带有 `Config#id` 的配置中获取,例如:`dubbo.application.demo-provider.name` 。
String value = null;
if (config.getId() != null && config.getId().length() > 0) {
String pn = prefix + config.getId() + "." + property;// 带有 `Config#id`
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
// 【启动参数变量】获取不到,其次不带 `Config#id` 的配置中获取,例如:`dubbo.application.name` 。
if (value == null || value.length() == 0) {
String pn = prefix + property;//不带 `Config#id`
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
if (value == null || value.length() == 0) {
// 覆盖优先级为:启动参数变量 > XML 配置 > properties 配置,因此需要使用 getter 判断 XML 是否已经设置
Method getter;
try {
getter = config.getClass().getMethod("get" + name.substring(3));
} catch (NoSuchMethodException e) {
try {
getter = config.getClass().getMethod("is" + name.substring(3));
} catch (NoSuchMethodException e2) {
getter = null;
}
}
if (getter != null) {
if (getter.invoke(config) == null) {// 使用 getter 判断 XML 是否已经设置
// 【properties 配置】优先从带有 `Config#id` 的配置中获取,例如:`dubbo.application.demo-provider.name` 。
if (config.getId() != null && config.getId().length() > 0) {
value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
}
// 【properties 配置】获取不到,其次不带 `Config#id` 的配置中获取,例如:`dubbo.application.name` 。
if (value == null || value.length() == 0) {
value = ConfigUtils.getProperty(prefix + property);
}
// 【properties 配置】老版本兼容,获取不到,最后不带 `Config#id` 的配置中获取,例如:`dubbo.protocol.name` 。
if (value == null || value.length() == 0) {
String legacyKey = legacyProperties.get(prefix + property);
if (legacyKey != null && legacyKey.length() > 0) {
value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
}
}
}
}
}
// 获取到值,进行反射设置。
if (value != null && value.length() > 0) {
method.invoke(config, convertPrimitive(method.getParameterTypes()[0], value));
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);//逻辑中间发生异常,不抛出异常,仅打印错误日志。
}
}
step1:从jvm启动参数获取:
// 【启动参数变量】获取不到,其次不带 `Config#id` 的配置中获取,例如:`dubbo.application.name` 。
if (value == null || value.length() == 0) {
String pn = prefix + property;//不带 `Config#id`
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
step2:判断 XML 是否已经设置:
if (getter.invoke(config) == null) {// 使用 getter 判断 XML 是否已经设置
step3:ConfigUtils.getProperty()从获取dubbo.properties参数
if (getter != null) {
if (getter.invoke(config) == null) {// 使用 getter 判断 XML 是否已经设置
// 【properties 配置】优先从带有 `Config#id` 的配置中获取,例如:`dubbo.application.demo-provider.name` 。
if (config.getId() != null && config.getId().length() > 0) {
value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
}
// 【properties 配置】获取不到,其次不带 `Config#id` 的配置中获取,例如:`dubbo.application.name` 。
if (value == null || value.length() == 0) {
value = ConfigUtils.getProperty(prefix + property);
}
// 【properties 配置】老版本兼容,获取不到,最后不带 `Config#id` 的配置中获取,例如:`dubbo.protocol.name` 。
if (value == null || value.length() == 0) {
String legacyKey = legacyProperties.get(prefix + property);
if (legacyKey != null && legacyKey.length() > 0) {
value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
}
}
}
注意:
@From:《dubbo文档-配置-属性配置》
1.如果 classpath 根目录下存在多个 dubbo.properties,比如多个 jar 包中有 dubbo.properties,Dubbo 会任意加载,并打印 Error 日志,后续可能改为抛异常。 ↩
2.协议的 id 没配时,缺省使用协议名作为 id ↩