SpringCloud 應用 Nacos 配置中心注解
前言
在 Spring Cloud 應用中可以非常低成本地集成 Nacos 實現(xiàn)配置動態(tài)刷新,在應用程序代碼中通過 Spring 官方的注解 @Value 和 @ConfigurationProperties,引用 Spring enviroment 上下文中的屬性值,關于這部分的介紹可以參照《Spring Cloud+Nacos+KMS 動態(tài)配置最佳實踐》一文,這種用法的最大優(yōu)點是無代碼層面侵入性,但也存在諸多限制,比如:
- Nacos 中配置是作為 Spring 上下文 enviroment 的屬性源之一,獲取屬性時會收到其他屬性源的干擾,比如通過 JVM 參數(shù)和環(huán)境變量注入的屬性優(yōu)先級比 Nacos 中的更高。
- 通過 spring.config.import 導入多個 Nacos 配置時,其中相同的 key 對應的屬性只會有一個生效,需要控制多個屬性源的 key 不重復或者處理因優(yōu)先級問題導致的屬性覆蓋問題。無法精準獲取指定 Nacos 配置中的屬性。
- 無法將 Nacos 配置自動注入對象類型的字段。
- 只能被動接受配置最終內(nèi)容,無法在配置變更時對配置進行二次處理或者觸發(fā)其他業(yè)務動作,無法感知指定的屬性變更前后的詳細信息。
- 通過 @Value 注解引用的配置要支持動態(tài)刷新,需要在 SpringBean 上添加 @RefreshScope,配置更新時會先將 Bean 銷毀再重新創(chuàng)建新的 Bean,使用不當易產(chǎn)生線上問題。
為了解決以上問題,提升應用接入 Nacos 配置中心的易用性,Spring Cloud Alibaba 發(fā)布一套全新的 Nacos 配置中心的注解。
- @NacosConfig:作用于 SpringBean 的字段,將 Nacos 中指定的配置注入字段;作用于 SpringBean Class,將 Nacos 中指定的配置注入 Bean 的屬性中;作用于 FactoryBean 方法,將 Nacos 中指定的配置注入 Bean 的屬性中,不依賴 RefreshScope 注解即可生效。
- @NacosConfigListener:作用于 SpringBean 的方法,在 Nacos 中的配置發(fā)生變化時,以方法參數(shù)形式接受變更后的最新配置內(nèi)容,支持以對象類型接收結果。
- @NacosConfigKeysListener:作用于 SpringBean 的方法,在 Nacos 中的配置的指定屬性 key 集合發(fā)生變化時,以方法參數(shù) ConfigChangeEvent 接受變更前后的屬性值。
以下將詳細介紹三種注解的用法。
@NacosConfig 注解用法介紹
如果配置格式為 yaml 和 properties,也可以額外指定 key 字段名,加載指定 key 對應的屬性值。
注解字段解釋
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface NacosConfig {
String group();
String dataId();
String key() default "";
String defaultValue() default "";
}
- group:引入的配置所屬分組。
- dataId: 引入的配置 DataId。
- key: 配置格式為 yaml 和 properties 時,也可以額外指定 key 字段名,加載指定 key 對應的屬性值。
- defaultValue:當配置不存在或者配置內(nèi)指定 key 屬性為空時,加載的默認值。
訪問 nacos 的全局參數(shù)通過 application.properties 中 spring.cloud.nacos.config.server-addr, spring.cloud.nacos.config.namespace 參數(shù)配置。通過 spring.config.import 導入的配置作用于 Spring 的 @Value 引用屬性源,和通過注解引入的方式相互獨立,但其內(nèi)部是共享同一個 NacosClient 對象,并且訪問的是同一個 Nacos 實例以及同一個命名空間下的配置。
用法示例
1. 加載完整配置內(nèi)容至 String 類型字段
@NacosConfig(dataId = "SampleApp.application.properties", group = "default")
String configContent;
將 dataId=SampleApp.application.properties,group = default 對應的配置的完整內(nèi)容注入到 configContent 字段。
2. 加載配置中的指定 key 屬性至基礎類型字段
@NacosConfig(dataId = "SampleApp.application.properties", group = "default", key = "useCache", defaultValue = "false")
boolean booleanValue;
將 dataId=SampleApp.application.properties,group = default 對應的配置中的 useCache 屬性值注入到 booleanValue 字段。
支持 int, long,float,double,boolean 5 種基礎類型以及其封裝類型。
3. 加載 JSON 格式配置至基礎類型數(shù)組字段
@NacosConfig(dataId = "scoreintArray.json", group = "default")
int[] scores;
將 dataId=scoreintArray.json,group = default 對應的 json 格式配置注入到 scores 字段,需保證配置格式可正常反序列化。
支持 int, long,float,double,boolean 5 種基礎類型數(shù)組以及其封裝類型。
配置需以 .json 結尾且配置內(nèi)容為 json 格式。
4. 加載配置至 Properties 類型字段
@NacosConfig(dataId = "SampleApp.application.properties", group = "default")
Properties properties = new Properties();
將 dataId = "SampleApp.application.properties", group = "default" 的配置內(nèi)容注入到 properties 字段中。可以通過 properties.getProperty 方法獲取其內(nèi)部屬性值。當遠端 Nacos 配置發(fā)生變化中,properties 對象會被替換,引用也會更新。
支持 properties 及 yaml 格式,dataId 需以 properties,yaml,yml 結尾,其中 yaml 格式中不能包含數(shù)組列表格式屬性。
5. 加載至自定義 JavaBean 字段
@NacosConfig(dataId = "myobject.json", group = "default")
MyObject json2Object;
@NacosConfig(dataId = "myobjectArray.json", group = "default")
MyObject[] json2ObjectArray;
@NacosConfig(dataId = "myobjectArray.json", group = "default")
List<MyObject> json2ObjectList;
@NacosConfig(dataId = "myobjectMap.json", group = "default")
Map<String, MyObject> json2ObjectMap;
將 dataId = "myobject.json", group = "default" 的配置加載至 json2Object 字段。
將 dataId = "myobjectArray.json", group = "default" 的 json 數(shù)組/列表格式的配置加載至 json2ObjectArray 字段。
將 dataId = "myobjectArray.json", group = "default" 的 json 數(shù)組/列表格式的配置加載至 json2ObjectList 字段。
將 dataId = "myobjectMap.json", group = "default" 的 json map 格式的配置加載至 json2ObjectMap 字段。
支持自定義數(shù)組,集合類型,支持自動根據(jù)指定泛型進行反序列化。
當遠端 Nacos 配置發(fā)生變化中,字段對應的對象會被替換,引用也會更新。
6. 加載 JSON 格式配置至 SpringBean
@Component
@NacosConfig(dataId = "myobject.json", group = "default")
public class MyObject {
String name;
String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
將 dataId = "myobject.json", group = "default" 的 JSON 配置加載至指定 SpringBean 的字段中,配置中的 JSON 屬性名需要和 SpringBean 中字段完全一致,且保證每個字段有 getter 及 setter 方法。
@NacosConfig 所在的類必須被發(fā)布為一個 SpringBean 才能生效。
@NacosConfig 類級別的注入優(yōu)先級高于字段級別,如在其 Bean 內(nèi)部字段中額外添加 @NacosConfig 將不會生效。
7. 加載 JSON 格式配置至工廠 Bean
public class SampleConfiguration {
@NacosConfig(dataId = "myobject1.json", group = "default")
@Bean
public MyObject bean1(){
return new MyObject();
}
@NacosConfig(dataId = "myobject2.json", group = "default")
@Bean
public MyObject bean2(){
return new MyObject();
}
}
將 dataId = "myobject1.json", group = "default" 的配置內(nèi)容加載至 beanName=bean1 的 MyObject 類型的 SpringBean 中。
將 dataId = "myobject2.json", group = "default" 的配置內(nèi)容加載至 beanName=bean2 的 MyObject 類型的 SpringBean 中。
必須配合 @Bean 注解使用。
@NacosConfigListener 注解用法介紹
如果希望在 Bean 初始化時接受初始回調(diào),可以設置 initNotify=true,默認為 false。
以下示例中的回調(diào)方法名均為示例,方法名可以自定義。
用法示例
1. String 參數(shù)方法接收原生配置內(nèi)容
@NacosConfigListener(dataId = "myobjectArray.json", group = "default")
private void fullContentChanged(String content) {
System.out.println("receive :" + content);
}
當 dataId = "myobjectArray.json", group = "default" 的配置發(fā)生變更時,將完整內(nèi)容以 content 參數(shù)回調(diào) fullContentChanged 方法。
2. 基礎類型參數(shù)方法接受指定 key 值內(nèi)容
@NacosConfigListener(dataId = "SampleApp.application.properties", group = "default",key="score")
private void scoreChanged(int score) {
System.out.println("receive :" + score);
}
當 dataId = "SampleApp.application.properties", group = "default" 的配置中key="score" 的屬性值發(fā)生變更時,將對應屬性值 score 參數(shù)回調(diào) scoreChanged 方法。
支持 int, long,float,double,boolean 5 種基礎類型。
3. 基礎類型數(shù)組參數(shù)方法接受 JSON 格式配置內(nèi)容
@NacosConfigListener(dataId = "scoresArray.json", group = "default")
private void scoresChanged(int[] scores) {
System.out.println("receive :" + scores);
}
當 dataId = "scoresArray.json", group = "default" 的 JSON 格式配置發(fā)生變更時,將配置內(nèi)容反序列化為基礎類型數(shù)組對象以 scores 參數(shù)回調(diào) scoresChanged 方法。
支持 int, long,float,double,boolean 5 種基礎類型數(shù)組。
配置 dataId 必須以 .json 結尾,并且配置內(nèi)容必須為 json 數(shù)組格式。
4. Properties 參數(shù)方法接受屬性參數(shù)
@NacosConfigListener(dataId = "SampleApp.application.properties", group = "default")
private void propertiesChanged(Properties properties) {
System.out.println("receive :" + properties);
}
當 dataId = "SampleApp.application.properties", group = "default" 的 properties 格式配置發(fā)生變更時,將配置內(nèi)容解析為Properties對象以 Properties 類型參數(shù)回調(diào) propertiesChanged 方法。
5. 自定義 Java Bean 參數(shù)
@NacosConfigListener(dataId = "myobject.json", group = "default")
private void myObjectChanged(MyObject object) {
System.out.println("receive :" + object);
}
@NacosConfigListener(dataId = "myobjectArray.json", group = "default")
private void myObjectArrayChanged(MyObject[] objectArray) {
System.out.println("receive :" + objectArray);
}
@NacosConfigListener(dataId = "myobjectArray.json", group = "default")
private void myObjectListChanged(List<MyObject> objectList) {
System.out.println("receive :" + objectList);
}
@NacosConfigListener(dataId = "myobjectMap.json", group = "default")
private void myObjectMapChanged(Map<String,MyObject> objectMap) {
System.out.println("receive :" + objectMap);
}
當指定配置內(nèi)容發(fā)生變更時,以對象,對象數(shù)組,對象列表,對象模式類型回調(diào)方法。
支持自定義數(shù)組,集合類型,自動根據(jù)指定泛型進行反序列化。
@NacosConfigKeysListener 注解用法介紹
通過 interestedKeys 指定監(jiān)聽的 keys 集合,通過 interestedKeyPrefixes 指定需要監(jiān)聽的 key 前綴集合,如果符合任意任一條件的 key 發(fā)生變化時都會觸發(fā)回調(diào)。
用法示例
@NacosConfigKeysListener(dataId = "SampleApp.122110test.properties", group = "default", interestedKeys = {
"useLocalCache,"}, interestedKeyPrefixes = {"122110."})
private void onKeysChangeSingle(ConfigChangeEvent changeEvent) {
System.out.println("interestedKeyPrefixes:nacos." + changeEvent.getChangeItems());
}
支持 properties 及 yaml 格式,dataId 需以 properties,yaml,yml 結尾。
結語
當前 Spring Cloud Alibaba 在全系列版本(包括 2.2.x,2021.x,2022.x,2023.x)中都已經(jīng)正式發(fā)布新版本支持以上注解。
- 2023.x 系列需升級版本至 2023.0.3.2
- 2022.x 系列需升級版本至 2022.0.0.2
- 2021.x 系列需升級版本至 2021.0.6.2
- 2.2.x 系列需升級至 2.2.11
其中在 2023.x 版本中,我們對 spring-cloud-alibaba 配置模塊進行了重構,單獨抽取了 spring-alibaba-nacos-config 模塊,該模塊不依賴 SpringCloud 框架,所以基于 SpringBoot3 的應用通過單獨引入 spring-alibaba-nacos-config 也可以使用 @Value 引用 Nacos 中的配置以及本文介紹的三種注解。
#pom.xml中引入spring-alibaba-nacos-config依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-alibaba-nacos-config</artifactId>
<version>2023.0.3.2</version>
</dependency>
#application.properties中添加如下配置
spring.config.import=nacos:optional:nacos:{此處按需替換為需導入的nacos dataId}?group={此處按需替換為需導入的nacos group}&refreshEnabled=true
spring.nacos.config.server-addr={此處按需替換為nacos的serverAddr}
spring.nacos.config.namespace={此處按需替換為nacos的命名空間,public請?zhí)羁諁
//其他參數(shù)請參照sca官方文檔,將spring.cloud.nacos前綴替換為spring.nacos即可,本文中的注解用法完全一致