外部化配置(External Configuration)

Dubbo 注解驱动例子中,无论是服务提供方,还是服务消费方,均需要转配相关配置Bean:

@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
       applicationConfig.setName("dubbo-annotation-consumer");
return applicationConfig;
}

虽然实现类似于 ProviderConfiguration 和 ConsumerConfiguration 这样的 Spring@Configuration Bean 成本并不高,不过通过 Java Code 的方式定义配置 Bean,或多或少是一种 Hard Code(硬编码)的行为,缺少弹性。

尽管在 Spring 应用中,可以通过 @Value 或者 Environment 的方式获取外部配置,其代码简洁性以及类型转换灵活性存在明显的不足。因此,Spring Boot 提出了外部化配置(External Configuration)的感念,即通过程序以外的配置源,动态地绑定指定类型。

随着 Spring Boot / Spring Cloud 应用的流行,开发人员逐渐地接受并且使用 Spring Boot 外部化配置(External Configuration),即通过 application.properties 或者bootstrap.properties 装配配置 Bean。

下列表格记录了 Dubbo 内置配置类:

通过申明对应的 Spring 扩展标签,在 Spring 应用上下文中将自动生成相应的配置 Bean。

在 Dubbo 官方用户手册的“属性配置”章节中, dubbo.properties 配置属性能够映射到ApplicationConfig 、 ProtocolConfig 以及 RegistryConfig 的字段。从某种意义上来说, dubbo.properties 也是 Dubbo 的外部化配置。

其中,引用“映射规则”的内容:

映射规则

将 XML 配置的标签名,加属性名,用点分隔,多个属性拆成多行

比如: dubbo.application.name=foo等价于
<dubbo:applicationname="foo"/> 

比如: dubbo.registry.address=10.20.153.10:9090等价于
<dubbo:registryaddress="10.20.153.10:9090"/>

如果 XML 有多行同名标签配置,可用 id 号区分,如果没有 id 号将对所有同名标签生效

比如: dubbo.protocol.rmi.port=1234等价于
<dubbo:protocolid="rmi"name="rmi"port="1099"/>2

比如: dubbo.registry.china.address=10.20.153.10:9090等价于
<dubbo:registryid="china"address="10.20.153.10:9090"/>

下面是 dubbo.properties 的一个典型配置:

dubbo.application.name=foo
dubbo.application.owner=bar
dubbo.registry.address=10.20.153.10:9090

根据“映射规则”,Dubbo 即支持单配置 Bean 映射,也支持多 Bean 映射。综合以上需求,既要兼容 Dubbo 已有的一个或多个 Bean 字段映射绑定,也支持外部化配置。

特别提醒:外部化配置(External Configuration)并非 Spring Boot 特有,即使在 Spring Framework 场景下亦能支持。也就是说 Dubbo 外部化配置即可在 Spring Framework 中工作,也能在 Spring Boot 中运行。

Dubbo 外部化配置(External Configuration) 支持起始版本为: 2.5.8

@EnableDubboConfig

起始版本: 2.5.8

使用说明

@EnableDubboConfig 定义

public @interface EnableDubboConfig {
/**
    * It indicates whether binding to multiple Spring Beans.
    *
    * @return the default value is <code>false</code>
    * @revised 2.5.9
    */
boolean multiple() default false;
}
  • multiple : 表示是否支持多Dubbo 配置 Bean 绑定。默认值为 false ,即单 Dubbo 配置 Bean 绑定

单 Dubbo 配置 Bean 绑定

为了更好地向下兼容, @EnableDubboConfig 提供外部化配置属性与 Dubbo 配置类之间的绑定,其中映射关系如下:

当标注 @EnableDubboConfig 的类被扫描注册后,同时 Spring(Spring Boot)应用配置( PropertySources)中存在 dubbo.application.* 时, ApplicationConfigBean 将被注册到在 Spring 上下文。否则,不会被注册。如果出现 dubbo.registry.*的配置,那么, RegistryConfig Bean 将会创建,以此类推。即按需装配 Dubbo 配置 Bean。

如果需要指定配置 Bean的 id,可通过 **.id 属性设置,以 dubbo.application 为例:

## application
dubbo.application.id = applicationBean
dubbo.application.name = dubbo-demo-application

以上配置等同于以下 Java Config Bean:

@Bean("applicationBean")
public ApplicationConfig applicationBean() {
ApplicationConfig applicationConfig = new ApplicationConfig();
       applicationConfig.setName("dubbo-demo-application");
return applicationConfig;
}

大致上配置属性与配置类绑定模式 - dubbo.application.* 映射到ApplicationConfig 中的字段。

注:当配置属性名称无法在配置类中找到字段时,将会忽略绑定

多 Dubbo 配置 Bean 绑定

Dubbo @Service 和 @Reference 允许 Dubbo 应用关联 ApplicationConfig Bean 或者指定多个 RegistryConfig Bean 等能力。换句话说,Dubbo 应用上下文中可能存在多个 ApplicationConfig 等 Bean定义。

为了适应以上需要,因此从Dubbo 2.5.9 开始, @EnableDubboConfig 支持多 Dubbo 配置 Bean 绑定,同时按照业界规约标准,与单 Dubbo 配置 Bean 绑定约定不同,配置属性前缀均为英文复数形式:

详情请参考 : https://github.com/alibaba/dubbo/issues/1141

dubbo.applications
dubbo.modules
dubbo.registries
dubbo.protocols
dubbo.monitors
dubbo.providers
dubbo.consumers

以 dubbo.applications 为例,基本的模式如下:

dubbo.applications.${bean-name}.property-name = ${property-value}

在单 Dubbo 配置 Bean 绑定时,可以通过指定 id 属性的方式,定义ApplicationConfig Bean 的ID,即 dubbo.application.id

而在多 Dubbo 配置 Bean 绑定时,Bean ID 则由 dubbo.applications.与属性字段名称( .property-name)之间的字符来表达。

如下配置:

# multiple Bean definition
dubbo.applications.applicationBean.name = dubbo-demo-application
dubbo.applications.applicationBean2.name = dubbo-demo-application2
dubbo.applications.applicationBean3.name = dubbo-demo-application3

该配置内容中,绑定了三个 ApplicationConfig Bean,分别是 applicationBeanapplicationBean2以及 applicationBean3

示例说明

@EnableDubboConfig 的使用方法很简答, 再次强调一点,当规约的外部配置存在时,相应的 Dubbo 配置类 才会提升为 Spring Bean。简言之,按需装配。

单 Dubbo 配置 Bean 绑定

外部化配置文件

将以下内容的外部化配置文件物理路径为: classpath:/META-INF/config.properties:

# 单 Dubbo 配置 Bean 绑定
## application
dubbo.application.id = applicationBean
dubbo.application.name = dubbo-demo-application
## module
dubbo.module.id = moduleBean
dubbo.module.name = dubbo-demo-module
## registry
dubbo.registry.address = zookeeper://192.168.99.100:32770
## protocol
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20880
## monitor
dubbo.monitor.address = zookeeper://127.0.0.1:32770
## provider
dubbo.provider.host = 127.0.0.1
## consumer
dubbo.consumer.client = netty

@EnableDubboConfig 配置 Bean

/**
* Dubbo 配置 Bean
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDubboConfig
@PropertySource("META-INF/config.properties")
@Configuration
public class DubboConfiguration {
}

实现引导类

/**
* Dubbo 配置引导类
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboConfigurationBootstrap {
public static void main(String[] args) {
// 创建配置上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册当前配置 Bean
       context.register(DubboConfiguration.class);
       context.refresh();
// application
ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class);
System.out.printf("applicationBean.name = %s \n", applicationConfig.getName());
// module
ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class);
System.out.printf("moduleBean.name = %s \n", moduleConfig.getName());
// registry
RegistryConfig registryConfig = context.getBean(RegistryConfig.class);
System.out.printf("registryConfig.name = %s \n", registryConfig.getAddress());
// protocol
ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class);
System.out.printf("protocolConfig.name = %s \n", protocolConfig.getName());
System.out.printf("protocolConfig.port = %s \n", protocolConfig.getPort());
// monitor
MonitorConfig monitorConfig = context.getBean(MonitorConfig.class);
System.out.printf("monitorConfig.name = %s \n", monitorConfig.getAddress());
// provider
ProviderConfig providerConfig = context.getBean(ProviderConfig.class);
System.out.printf("providerConfig.name = %s \n", providerConfig.getHost());
// consumer
ConsumerConfig consumerConfig = context.getBean(ConsumerConfig.class);
System.out.printf("consumerConfig.name = %s \n", consumerConfig.getClient());
}
}

执行结果

applicationBean.name = dubbo-demo-application
moduleBean.name = dubbo-demo-module
registryConfig.name = zookeeper://192.168.99.100:32770
protocolConfig.name = dubbo
protocolConfig.port = 20880
monitorConfig.name = zookeeper://127.0.0.1:32770
providerConfig.name = 127.0.0.1
consumerConfig.name = netty

不难发现, @EnableDubboConfig 配置 Bean 配合外部化文件 classpath:/META-INF/config.properties,与执行输出内容相同。

多 Dubbo 配置 Bean 绑定

外部化配置文件

将以下内容的外部化配置文件物理路径为: classpath:/META-INF/multiple-config.properties:

# 多 Dubbo 配置 Bean 绑定
## dubbo.applications
dubbo.applications.applicationBean.name = dubbo-demo-application
dubbo.applications.applicationBean2.name = dubbo-demo-application2
dubbo.applications.applicationBean3.name = dubbo-demo-application3

@EnableDubboConfig 配置 Bean(多)

@EnableDubboConfig(multiple = true)
@PropertySource("META-INF/multiple-config.properties")
private static class DubboMultipleConfiguration {
}

实现引导类

/**
* Dubbo 配置引导类
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboConfigurationBootstrap {
public static void main(String[] args) {
// 创建配置上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册当前配置 Bean
       context.register(DubboMultipleConfiguration.class);
       context.refresh();
// 获取 ApplicationConfig Bean:"applicationBean"、"applicationBean2" 和 "applicationBean3"
ApplicationConfig applicationBean = context.getBean("applicationBean", ApplicationConfig.class);
ApplicationConfig applicationBean2 = context.getBean("applicationBean2", ApplicationConfig.class);
ApplicationConfig applicationBean3 = context.getBean("applicationBean3", ApplicationConfig.class);
System.out.printf("applicationBean.name = %s \n", applicationBean.getName());
System.out.printf("applicationBean2.name = %s \n", applicationBean2.getName());
System.out.printf("applicationBean3.name = %s \n", applicationBean3.getName());
}
}

执行结果

applicationBean.name = dubbo-demo-application
applicationBean2.name = dubbo-demo-application2
applicationBean3.name = dubbo-demo-application3

@EnableDubboConfig(multiple=true) 执行后,运行结果说明 ApplicationConfigBean 以及 ID 的定义方式。

@EnableDubboConfigBinding & @EnableDubboConfigBindings

@EnableDubboConfig适合绝大多数外部化配置场景,然而无论是单 Bean 绑定,还是多 Bean 绑定,其外部化配置属性前缀是固化的,如 dubbo.application 以及dubbo.applications 。

当应用需要自定义外部化配置属性前缀, @EnableDubboConfigBinding能提供更大的弹性,支持单个外部化配置属性前缀( prefix) 与 Dubbo 配置 Bean 类型(AbstractConfig 子类)绑定,如果需要多次绑定时,可使用@EnableDubboConfigBindings

尽管 Dubbo 推荐使用 Java 8 ,然而实际的情况,运行时的 JDK 的版本可能从 6到8 均有。因此,  @EnableDubboConfigBinding 没有实现java.lang.annotation.Repeatable,即允许实现类不支持重复标注 @EnableDubboConfigBinding

@EnableDubboConfigBinding 在支持外部化配置属性与 Dubbo 配置类绑定时,与 Dubbo 过去的映射行为不同,被绑定的 Dubbo 配置类将会提升为 Spring Bean,无需提前装配 Dubbo 配置类。同时,支持多 Dubbo 配置Bean 装配。其 Bean 的绑定规则与@EnableDubboConfig一致。

起始版本: 2.5.8

使用说明

@EnableDubboConfigBinding 定义

public @interface EnableDubboConfigBinding {
/**
    * The name prefix of the properties that are valid to bind to {@link AbstractConfig Dubbo Config}.
    *
    * @return the name prefix of the properties to bind
    */
String prefix();
/**
    * @return The binding type of {@link AbstractConfig Dubbo Config}.
    * @see AbstractConfig
    * @see ApplicationConfig
    * @see ModuleConfig
    * @see RegistryConfig
    */
Class<? extends AbstractConfig> type();
/**
    * It indicates whether {@link #prefix()} binding to multiple Spring Beans.
    *
    * @return the default value is <code>false</code>
    */
boolean multiple() default false;
}
  • prefix() : 指定待绑定 Dubbo 配置类的外部化配置属性的前缀,比如dubbo.application 为 ApplicationConfig 的外部化配置属性的前缀。prefix() 支持占位符(Placeholder), 并且其关联前缀值是否以"." 作为结尾字符是可选的,即 prefix()="dubbo.application" 与prefix()="dubbo.application."效果相同

  • type() : 指定 Dubbo 配置类,所有 AbstractConfig 的实现子类即可,如ApplicationConfig 、 RegistryConfig 以及 ProtocolConfig 等

  • multiple() : 表明是否需要将 prefix() 作为多个 type() 类型的 Spring Bean 外部化配置属性。默认值为 false,即默认支持单个类型的 Spring 配置 Bean

假设标注 @EnableDubboConfigBinding 的实现类被 Spring 应用上下文扫描并且注册后,其中 prefix() = dubbo.app 、 type() = ApplicationConfig.class ,且外部配置内容为:

dubbo.app.id = applicationBean
dubbo.app.name = dubbo-demo-application

Spring 应用上下文启动后,一个 ID 为 "applicationBean" 的 ApplicationConfig Bean 被初始化,其 name 字段被设置为 "dubbo-demo-application"。

EnableDubboConfigBindings 定义

public @interface EnableDubboConfigBindings {
/**
    * The value of {@link EnableDubboConfigBindings}
    *
    * @return non-null
    */
EnableDubboConfigBinding[] value();
}
  • value : 指定多个 EnableDubboConfigBinding,用于实现外部化配置属性前缀(prefix) 与 Dubbo 配置 Bean 类型( AbstractConfig 子类)绑定。

示例说明

外部化配置文件

将以下内容的外部化配置文件物理路径为: classpath:/META-INF/bindings.properties

# classpath:/META-INF/bindings.properties
## 占位符值 : ApplicationConfig 外部配置属性前缀
applications.prefix = dubbo.apps.
## 多 ApplicationConfig Bean 绑定
dubbo.apps.applicationBean.name = dubbo-demo-application
dubbo.apps.applicationBean2.name = dubbo-demo-application2
dubbo.apps.applicationBean3.name = dubbo-demo-application3
## 单 ModuleConfig Bean 绑定
dubbo.module.id = moduleBean
dubbo.module.name = dubbo-demo-module
## 单 RegistryConfig Bean 绑定
dubbo.registry.address = zookeeper://192.168.99.100:32770

EnableDubboConfigBindings 配置 Bean

DubboConfiguration 作为 Dubbo 配置 Bean,除通过 @EnableDubboConfigBinding绑定之外,还需要 @PropertySource 指定外部化配置文件( classpath:/META-INF/bindings.properties):

/**
* Dubbo 配置 Bean
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "${applications.prefix}",
               type = ApplicationConfig.class, multiple = true), // 多 ApplicationConfig Bean 绑定
@EnableDubboConfigBinding(prefix = "dubbo.module", // 不带 "." 后缀
               type = ModuleConfig.class), // 单 ModuleConfig Bean 绑定
@EnableDubboConfigBinding(prefix = "dubbo.registry.", // 带 "." 后缀
               type = RegistryConfig.class) // 单 RegistryConfig Bean 绑定
})
@PropertySource("META-INF/bindings.properties")
@Configuration
public class DubboConfiguration {
}

实现引导类

通过之前的使用说明,当 EnableDubboConfigBinding 将外部配置化文件classpath:/META-INF/dubbo.properties 绑定到 ApplicationConfig后,其中 Spring Bean "applicationBean" 的 name 字段被设置成 "dubbo-demo-application"。同时, EnableDubboConfigBinding 所标注的 DubboConfiguration 需要被 Sring 应用上下文注册:

/**
* Dubbo 配置引导类
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboConfigurationBootstrap {
public static void main(String[] args) {
// 创建配置上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册当前配置 Bean
       context.register(DubboConfiguration.class);
       context.refresh();
// 获取 ApplicationConfig Bean:"applicationBean"、"applicationBean2" 和 "applicationBean3"
ApplicationConfig applicationBean = context.getBean("applicationBean", ApplicationConfig.class);
ApplicationConfig applicationBean2 = context.getBean("applicationBean2", ApplicationConfig.class);
ApplicationConfig applicationBean3 = context.getBean("applicationBean3", ApplicationConfig.class);
System.out.printf("applicationBean.name = %s \n", applicationBean.getName());
System.out.printf("applicationBean2.name = %s \n", applicationBean2.getName());
System.out.printf("applicationBean3.name = %s \n", applicationBean3.getName());
// 获取 ModuleConfig Bean:"moduleBean"
ModuleConfig moduleBean = context.getBean("moduleBean", ModuleConfig.class);
System.out.printf("moduleBean.name = %s \n", moduleBean.getName());
// 获取 RegistryConfig Bean
RegistryConfig registry = context.getBean(RegistryConfig.class);
System.out.printf("registry.address = %s \n", registry.getAddress());
}
}

运行结果

DubboConfigurationBootstrap 运行后控制台输出:

applicationBean.name = dubbo-demo-application
applicationBean2.name = dubbo-demo-application2
applicationBean3.name = dubbo-demo-application3
moduleBean.name = dubbo-demo-module
registry.address = zookeeper://192.168.99.100:32770

输出的内容与 classpath:/META-INF/bindings.properties 绑定的内容一致,符合期望。

作者:小马哥

链接:Dubbo 新编程模型之外部化配置

更多参考内容:http://www.roncoo.com/article/index?tn=Dubbo

Dubbo 新编程模型之外部化配置的更多相关文章

  1. Spring配置文件外部化配置及.properties的通用方法

    摘要:本文深入探讨了配置化文件(即.properties)的普遍应用方式.包括了Spring.一般的.远程的三种使用方案. 关键词:.properties, Spring, Disconf, Java ...

  2. SpringBoot官方文档学习(二)Externalized Configuration(外部化配置)

    Spring Boot允许您将配置外部化,以便可以在不同的环境中使用相同的应用程序代码.您可以使用属性文件.YAML文件.环境变量和命令行参数来具体化配置.属性值可以通过使用@Value注释直接注入b ...

  3. 曹工谈Spring Boot:Spring boot中怎么进行外部化配置,一不留神摔一跤;一路debug,原来是我太年轻了

    spring boot中怎么进行外部化配置,一不留神摔一跤:一路debug,原来是我太年轻了 背景 我们公司这边,目前都是spring boot项目,没有引入spring cloud config,也 ...

  4. Spring Boot外部化配置实战解析

    一.流程分析 1.1 入口程序 在 SpringApplication#run(String... args) 方法中,外部化配置关键流程分为以下四步 public ConfigurableAppli ...

  5. SpringBoot 正式环境必不可少的外部化配置

    前言 <[源码解析]凭什么?spring boot 一个 jar 就能开发 web 项目> 中有读者反应: 部署后运维很不方便,比较修改一个 IP 配置,需要重新打包. 这一点我是深有体会 ...

  6. Spring Boot 外部化配置(一)- Environment、ConfigFileApplicationListener

    目录 前言 1.起源 2.外部化配置的资源类型 3.外部化配置的核心 3.1 Environment 3.1.1.ConfigFileApplicationListener 3.1.2.关联 Spri ...

  7. Spring Boot 外部化配置(二) - @ConfigurationProperties 、@EnableConfigurationProperties

    目录 3.外部化配置的核心 3.2 @ConfigurationProperties 3.2.1 注册 Properties 配置类 3.2.2 绑定配置属性 3.1.3 ConfigurationP ...

  8. 玩转Spring Boot 自定义配置、导入XML配置与外部化配置

    玩转Spring Boot 自定义配置.导入XML配置与外部化配置       在这里我会全面介绍在Spring Boot里面如何自定义配置,更改Spring Boot默认的配置,以及介绍各配置的优先 ...

  9. 关于SpringBoot的外部化配置使用记录

    关于SpringBoot的外部化配置使用记录 声明: 若有任何纰漏.错误请不吝指出! 记录下使用SpringBoot配置时遇到的一些麻烦,虽然这种麻烦是因为知识匮乏导致的. 记录下避免一段时间后自己又 ...

随机推荐

  1. b继承a

    有下面这样的一段代码: function a(){ this.foo = function(){ console.log('foo'); } } var b = {}; 请问如何让b继承a? b.__ ...

  2. Nginx防盗链配置案例配置,Nginx的知识分享

    防盗链的含义:网站内容不在自己服务器上,而通过技术手段,绕过别人放广告有利益的最终页,直接在自己的有广告有利益的页面上向最终用户提供此内容. 常常是一些名不见经传的小网站来盗取一些有实力的大网站的地址 ...

  3. Linux下绝对经典的命令

    1.使用远程终端时,可以使用如下命令: screen tmux 2.下载文件可以使用如下命令: curl wget 3.压缩解压缩可以使用: tar .zip.rar 4.使用抓包工具 tcpdump ...

  4. tcping-安装

    1.LINUX安装方法: 下载地址: http://www.linuxco.de/tcping/tcping.html 不过LINUX操作比WINDOWS的差.建议还是使用WINDOWS版本. 2.W ...

  5. PHP die与exit的区别

    最近听见有人说die和exit区别,bula~bula.决心一探究竟. 翻了翻PHP 5.6的源码(源码的位置为zend目录下zend_language_scanner.l大约是1014~1020行) ...

  6. MySQL建立外键(Foreign Key)

    如果在最初建立表的时候就建立外键这样一般不会有什么问题,顺便说一下建立外键的时候,需要注意的地方. cascade方式在父表上update/delete记录时,同步update/delete掉子表的匹 ...

  7. 利用Jsonp实现跨域请求,spring MVC+JQuery

    1 什么是Jsonp? JSONP(JSON with Padding)是数据格式JSON的一种"使用模式",可以让网页从别的网域要数据.另一个解决这个问题的新方法是跨来源资源共享 ...

  8. 为何要部署IPV6

    ·IPv4的局限性:   1.地址空间的局限性:IP地址空间的危机由来已久,并正是升级到IPv6的主要动力.   2.安全性:IPv4在网络层没有安全性可言,安全性一直被认为是由网络层以上的层负责. ...

  9. chromedriver与chrome版本映射表(最新)

    selenium想在chrome进行跑,前提需要下载chromedriver,以下整理了chromedriver与chrome的对应关系表 chromedriver(下载地址):http://chro ...

  10. Java集合中的AbstractMap抽象类

    jdk1.8.0_144 AbstractMap抽象类实现了一些简单且通用的方法,本身并不难.但在这个方法中有两个方法非常值得关注,keySet和values方法源码的实现可以说是教科书式的典范. 抽 ...