在Dubbo 註解驅動例子中,無論是服務提供方,還是服務消費方,均需要轉配相關配置Bean: ...
外部化配置(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.*
時, ApplicationConfig
Bean 將被註冊到在 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,分別是 applicationBean
、applicationBean2
以及 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:[email protected]">Mercy</a> */ @EnableDubboConfig @PropertySource("META-INF/config.properties") @Configuration public class DubboConfiguration { }
實現引導類
/** * Dubbo 配置引導類 * * @author <a href="mailto:[email protected]">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:[email protected]">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)
執行後,運行結果說明 ApplicationConfig
Bean 以及 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:[email protected]">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:[email protected]">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
綁定的內容一致,符合期望。
作者:小馬哥
更多參考內容:http://www.roncoo.com/article/index?tn=Dubbo