By Zunfei Liu
In Spring Cloud applications, Nacos can be integrated at a very low cost to achieve dynamic configuration refresh. In the application code, the official Spring annotations @Value and @ConfigurationProperties can reference property values in the Spring environment context. For more information on this, please refer to the article Best Practices for Dynamic Configuration with Spring Cloud, Nacos, and KMS. The biggest advantage of this approach is that it is non-intrusive at the code level. However, it also has many limitations, such as:
• The configurations in Nacos serve as one of the property sources of the Spring context environment. When obtaining properties, they may be affected by other property sources. For example, properties injected through JVM parameters and environment variables have higher priority than those in Nacos.
• When multiple Nacos configurations are imported through spring.config.import, if there are identical keys among them, only one will take effect. You need to ensure that keys across multiple property sources do not duplicate or address the issue of property overwriting caused by priority issues. The properties in the specified Nacos configuration cannot be accurately obtained.
• The Nacos configurations cannot be automatically injected into fields of object types.
• Configuration changes can only be passively received. There's no support for secondary processing of configurations upon change or triggering other business actions. There's also no way to perceive detailed information about property changes before and after.
• To support dynamic refresh for configurations referenced by the @Value annotation, it is necessary to add @RefreshScope to the SpringBean. During configuration updates, this process first destroys the Bean and then recreates a new one, which can easily lead to issues in production if not used properly.
To resolve the preceding issues and facilitate application access to the Nacos configuration center, Spring Cloud Alibaba released a new set of annotations for the Nacos configuration center.
• @NacosConfig: When applied to a field of a SpringBean, this annotation injects the specified configuration from Nacos into the field; when applied to a SpringBean class, it injects the specified configuration from Nacos into the properties of the Bean; when applied to a FactoryBean method, it injects the specified configuration from Nacos into the properties of the Bean. It takes effect without relying on the @RefreshScope annotation.
• @NacosConfigListener: This annotation is used on methods within a SpringBean. it receives the latest configuration content after changes in Nacos as a method parameter and supports receiving results as object types.
• @NacosConfigKeysListener: This annotation is also used on methods within a SpringBean. When a specified set of property keys in Nacos configurations changes, the ConfigChangeEvent, which includes the property values before and after the change, is received as method parameters.
The usage of the three annotations will be described in detail below.
The @NacosConfig annotation allows you to inject specified Nacos configurations into the fields of a SpringBean. It can be applied to a single field of a SpringBean or to the SpringBean itself (including adding the annotation to the BeanClass and combining it with the @Bean annotation for FactoryBean injection mode). The injected target types support basic types, properties, and collections such as List<T>
, Set<T>
, Map<T>
generics, and any other custom Java Beans.
If the configuration format is YAML and properties, you can also specify an additional key field name to load the property value corresponding to the specified 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: The group to which the introduced configuration belongs.
• dataId: The DataId of the introduced configuration.
• key: If the configuration format is YAML and properties, you can also specify an additional key field name to load the property value corresponding to the specified key.
• defaultValue: The default value will be loaded when the configuration does not exist or the specified key property is empty.
The global parameters for accessing Nacos are configured through spring.cloud.nacos.config.server-addr and spring.cloud.nacos.config.namespace in application.properties. The configurations imported by spring.config.import are applied to the property source referenced by Spring's @Value and are independent of the configurations imported by annotations. However, they share the same NacosClient object and access the configurations in the same Nacos instance and namespace.
@NacosConfig(dataId = "SampleApp.application.properties", group = "default")
String configContent;
Inject the complete configuration content of dataId=SampleApp.application.properties, group = default
into the configContent field.
@NacosConfig(dataId = "SampleApp.application.properties", group = "default", key = "useCache", defaultValue = "false")
boolean booleanValue;
Inject the value of the useCache property from the configuration of dataId=SampleApp.application.properties, group = default
into the booleanValue field.
This supports the following five basic types and their encapsulated types: int, long, float, double, and boolean.
@NacosConfig(dataId = "scoreintArray.json", group = "default")
int[] scores;
Inject the JSON-formatted configuration of dataId= scoreintArray.json, group = default
into the scores field. The configuration format must be properly deserialized.
This supports arrays of the following five basic types and their encapsulated types: int, long, float, double, and boolean.
The configuration must end with. json, and the configuration content must be in JSON format.
@NacosConfig(dataId = "SampleApp.application.properties", group = "default")
Properties properties = new Properties();
Inject the configuration content of dataId = "SampleApp.application.properties", group = "default"
into the properties field. Its internal property values can be obtained through the properties.getProperty method. When the remote Nacos configuration changes, the properties object is replaced, and the reference is updated.
This supports properties and YAML formats. The dataId must end with .properties, .yaml, or .yml. The YAML format cannot contain properties in the array or list format.
@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;
Load the configuration of dataId = "myobject.json" and group = "default" into the json2Object field.
Load the configuration in the JSON array or list format of dataId = "myobjectArray.json" and group = "default" into the json2ObjectArray field.
Load the configuration in the JSON array or list format of dataId = "myobjectArray.json" and group = "default" into the json2ObjectList field.
Load the configuration in the JSON map format of dataId = "myobjectMap.json" and group = "default" into the json2ObjectMap field.
This supports custom arrays and collection types, with automatic deserialization based on the specified generics.
When the remote Nacos configuration changes, the object corresponding to the field is replaced, and the reference is updated.
@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;
}
}
Load the JSON configuration of dataId = "myobject.json" and group = "default" into the fields of the specified SpringBean. The JSON property names in the configuration must match the field names in the SpringBean exactly, and each field must have getter and setter methods.
The class annotated with @NacosConfig must be published as a SpringBean to take effect.
The injection priority of @NacosConfig at the class level is higher than at the field level. For example, when additional annotations are added to fields within its Bean, @NacosConfig will not take effect.
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();
}
}
Load the configuration content of dataId = "myobject1.json" and group = "default" into the SpringBean of the MyObject type with beanName=bean1.
Load the configuration content of dataId = "myobject2.json" and group = "default" into the SpringBean of the MyObject type with beanName=bean2.
This must be used in conjunction with the @Bean annotation.
The target of the @NacosConfig annotation is the field. It directly changes the property of the target field. When we need to perform secondary processing on the configuration content, the @NacosConfig annotation will not be competent. For example, when a property changes, the program is triggered to execute a business action or perform secondary processing in the code based on the changed configuration. In this case, the @NacosConfigListener annotation can be used to meet this demand. This annotation applies to the SpringBean method. When the specified configuration changes, the configuration content is called back to the specified method in the form of method parameters. Similar to the @NacosConfig annotation, method parameters support basic types, properties, arrays, collections, and custom JavaBeans.
If you want to accept the initial callback when the Bean is initialized, you can set initNotify=true, and the default is false.
The following callback method names are examples. You can customize the method name.
@NacosConfigListener(dataId = "myobjectArray.json", group = "default")
private void fullContentChanged(String content) {
System.out.println("receive :" + content);
}
When the configuration of dataId = "myobjectArray.json" and group = "default" changes, the complete content is passed as the content parameter to the fullContentChanged method.
@NacosConfigListener(dataId = "SampleApp.application.properties", group = "default",key="score")
private void scoreChanged(int score) {
System.out.println("receive :" + score);
}
When the property value of key="score" in the configuration of dataId = "SampleApp.application.properties" and group = "default" changes, the corresponding property value is passed as the score parameter to the scoreChanged method.
This supports the following 5 basic types: int, long, float, double, and boolean.
@NacosConfigListener(dataId = "scoresArray.json", group = "default")
private void scoresChanged(int[] scores) {
System.out.println("receive :" + scores);
}
When the JSON-formatted configuration of dataId = "scoresArray.json" and group = "default" changes, the configuration content is deserialized into a basic type array object and passed as the scores parameter to the scoresChanged method.
This supports arrays of the following five basic types: int, long, float, double, and boolean.
The dataId must end with .json, and the configuration content must be in JSON array format.
@NacosConfigListener(dataId = "SampleApp.application.properties", group = "default")
private void propertiesChanged(Properties properties) {
System.out.println("receive :" + properties);
}
When the properties-formatted configuration of dataId = "SampleApp.application.properties" and group = "default" changes, the configuration content is parsed into a Properties object and passed as a Properties type parameter to the propertiesChanged method.
@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);
}
When the specified configuration content changes, the methods are called back with objects, object arrays, object lists, and object maps.
This supports custom arrays and collection types, with automatic deserialization based on the specified generics.
This annotation supports configurations in properties and YAML formats. It allows you to receive the content before and after changes for specified keys through the ConfigChangeEvent parameter when these keys change.
You can specify the set of keys to listen to by using interestedKeys and key prefixes to listen to by using interestedKeyPrefixes. If any key that matches these conditions changes, the callback will be triggered.
@NacosConfigKeysListener(dataId = "SampleApp.122110test.properties", group = "default", interestedKeys = {
"useLocalCache,"}, interestedKeyPrefixes = {"122110."})
private void onKeysChangeSingle(ConfigChangeEvent changeEvent) {
System.out.println("interestedKeyPrefixes:nacos." + changeEvent.getChangeItems());
}
This supports properties and YAML formats. The dataId must end with .properties, .yaml, or .yml.
Spring Cloud Alibaba has officially released new versions across its entire series (including 2.2.x, 2021.x, 2022.x, and 2023.x) that support the annotations mentioned above.
• The 2023.x series needs to be upgraded to version 2023.0.3.2.
• The 2022.x series needs to be upgraded to version 2022.0.0.2.
• The 2021.x series needs to be upgraded to version 2021.0.6.2.
• The 2.2.x series needs to be upgraded to version 2.2.11.
In version 2023.x, we reconstructed the spring-cloud-alibaba configuration module and extracted the spring-alibaba-nacos-config module separately. This module does not depend on the SpringCloud framework. Therefore, applications based on SpringBoot 3 can use the @Value annotation to reference configurations in Nacos and the three annotations introduced in this article by simply including the spring-alibaba-nacos-config module.
# Introduce spring-alibaba-nacos-config dependencies into pom.xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-alibaba-nacos-config</artifactId>
<version>2023.0.3.2</version>
</dependency>
# Add the following configurations in application.properties
spring.config.import=nacos:optional:nacos:{ replace with the nacos dataId to import }?group={ replace with the nacos group to import }&refreshEnabled=true
spring.nacos.config.server-addr={ replace with the nacos serverAddr }
spring.nacos.config.namespace={ replace with your nacos namespace, leave empty for public }
// For other parameters, please refer to the official SCA documentation and replace the prefix spring.cloud.nacos with spring.nacos. The usage of the annotations introduced in this article remains the same.
[1] Nacos Official Website
https://nacos.io
[2] Nacos Github Main Repository
https://github.com/alibaba/nacos
[3] Ecological Group Repository
https://github.com/nacos-group
[4] Spring Cloud Alibaba
https://sca.aliyun.com/docs/2023/user-guide/nacos/quick-start/
[1] Nacos-GO-SDK
https://github.com/nacos-group/nacos-sdk-go
[2] Nacos-Python-SDK
https://github.com/nacos-group/nacos-sdk-python
[3] Nacos-Rust-SDK
https://github.com/nacos-group/nacos-sdk-rust
[4] Nacos C# SDK
https://github.com/nacos-group/nacos-sdk-csharp
[5] Nacos C++ SDK
https://github.com/nacos-group/nacos-sdk-cpp
[6] Nacos PHP-SDK
https://github.com/nacos-group/nacos-sdk-php
[7] Rust Nacos Server
https://github.com/nacos-group/r-nacos
Higress.ai Officially Launches: Effortlessly Unlock New AI Capabilities and Start Global Services
557 posts | 53 followers
FollowAlibaba Cloud Native Community - February 20, 2025
Alibaba Developer - June 25, 2021
Alibaba Cloud Native Community - October 31, 2023
Alibaba Cloud Native Community - March 11, 2024
Alibaba Clouder - March 26, 2019
Alibaba Cloud Native Community - April 24, 2023
557 posts | 53 followers
FollowFollow our step-by-step best practices guides to build your own business case.
Learn MoreAlibaba Cloud Function Compute is a fully-managed event-driven compute service. It allows you to focus on writing and uploading code without the need to manage infrastructure such as servers.
Learn MoreMulti-source metrics are aggregated to monitor the status of your business and services in real time.
Learn MoreHigh Performance Computing (HPC) and AI technology helps scientific research institutions to perform viral gene sequencing, conduct new drug research and development, and shorten the research and development cycle.
Learn MoreMore Posts by Alibaba Cloud Native Community