6.[dubbo源码解析]-属性配置

本文详细解析了Dubbo框架中的配置参数解析流程,包括provider配置入口、缺省配置的加载机制,以及属性配置与XML配置之间的映射规则和覆盖策略。重点介绍了配置加载的优先级和具体的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(前言:以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-id
  • xml:<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 ↩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值