foreword
The material of this article comes from the pit that a friend stepped on when integrating nacos2 as the configuration center for dynamic refresh. The problem he encountered at that time, the following screenshot
Because I was busy during that time, I found an answer that seemed to be a solution without reading the code of my friend's project and threw it away.
A friend added this configuration later, and the problem was not solved. I took a little time later and asked for his project code to look at it.
code example
Because his project is mainly a self-taught nacos project, and it does not involve any sensitive information. This article will directly take his project example to demonstrate
1. Project pom dependencies
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
Note: nacos server version is 2.1.1
2. The address of the nacos configuration center, which is configured in bootstrap.yml
spring:
cloud:
nacos:
server-addr: localhost:8848
3. The basic information of the project is configured in application.yml
#
spring:
application:
name: nacos-config
server:
port: 8030
4. Write a controller that needs to dynamically refresh the acquired value
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${user.userName:123}")
private String userName;
@RequestMapping("/get")
private String get(){
return userName;
}
}
5. The business project is configured as follows on the nacos server
The above is a complete code example of a friend. When we run the code, we will find that the userName of the controller cannot get a value. Interested friends, you can check the above code to find out the reason
Reason for not getting value
Foreshadowing of theoretical knowledge :
When we use the cglib dynamic proxy to call the target method, when the method is decorated with private, this is the dynamic proxy object. When the method is public or protected, this is the target object. In addition, the attribute refresh brushes the attributes of the target object, and the get method of the controller can be regarded as
@RequestMapping("/get")
private String get(){
return this.userName;
}
When we add the @RefreshScope annotation to the controller, if we do not change the value of the proxyMode property, it will generate a cglib dynamic proxy by default. When we call the get method, because get is a private method, we can see it as
@RequestMapping("/get")
private String get(){
return cglibProxy.userName;
}
At this time, this is the proxy object, and at this time userName is the userName of the proxy object, and the userName of the proxy object is a null value.
Solution
Method 1. Modify the proxyMode property of @RefreshScope
Change proxyMode to ScopedProxyMode.DEFAULT or ScopedProxyMode.NO
At this time, this is the target object, so it can get the value
Method 2: Change the modifier of the target method to public or protected
Example :
At this time, this is the target object, so it can get the value
3. Method 3: Use the attribute configuration class
@Configuration
@ConfigurationProperties(prefix = "user")
public class UserProperties {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
controller adjusted to
@RestController
@RequestMapping("/config")
public class ConfigController {
@Autowired
private UserProperties userProperties;
@RequestMapping("/get")
public String get(){
return userProperties.getUserName();
}
}
At this time, the controller can achieve dynamic refresh without adding @RefreshScope. Because @ConfigurationProperties on the property class itself has the characteristics of dynamic refresh
Summarize
This article is not a pit that @RefreshScope and nacos2 stepped on. It is mainly about the knowledge of dynamic agents. The title is a bit of a headline.
When some videos talk about nacos dynamic refresh, they basically use @RerfreshScope + @value on the controller to explain. In fact, a similar effect can be achieved using @ConfigurationProperties. If it is not integrated with springcloud, introduce the starter of the nacos configuration center, and use @NacosPropertySource + @NacosValue or @NacosRefresh to achieve dynamic refresh. Interested friends can try it
Finally, my friend also stepped on some pits in the process of building nacos2. Interested friends can view the following articles
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。