前言

在平时的开发中,我们一般使用 nacos 来作为项目的注册中心和配置中心,其中配置中心的动态化配置又是我们经常要用到的设置。配置一些经常发生变化的值特别好用,不需要重启项目,不需要修改代码,只需要在 nacos 的配置文件修改完成,保存一下就可以完成动态配置。这次我想专门记录一下对于 static 修饰的值如何获取到值与动态的刷新该值。

正常使用案例

首先我们先看一下最为经典的用法,也就是非 static 字段的使用,我首先在 nacos 中了如下这么一段配置。

image-20230525212738252

在代码中我是通过下面代码获取到 nacos 配置的值,我个人习惯用 @ConfigurationProperties 指定前缀并搭配 @Configuration 来使用,通过 set 来赋值,同时加一个注解 RefreshScope 来开启动态刷新。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "sora")
public class NacosVo implements Serializable {

@Serial
private static final long serialVersionUID = 8996476309916434072L;

public String name;
public String age;
public String sex;
}

 简单写一个方法,打印出来

1
2
3
4
5
6
7
8
@Resource
private NacosVo sora;

@GetMapping("value")
public Result getNacosValue() {
logger.info("nacos中对象:[{}]", sora);
return Result.success("成功");
}

通过日志我们可以看到获取成功且没有问题,修改值也可以及时更新,这里就不过多赘述,我们进入 static 部分

image-20230525213433195

image-20230525213606699

搭配 static 使用案例

现在我们把所有的字段变为 static,重启代码试一下是否还可以获取到值

1
2
3
4
5
6
7
8
9
10
11
12
13
@RefreshScope
@Data
@Configuration
@ConfigurationProperties(prefix = "sora")
public class NacosVo implements Serializable {

@Serial
private static final long serialVersionUID = 8996476309916434072L;

public static String name;
public static String age;
public static String sex;
}

很明显,获取到的对象是空的。可以判断出我们修改成了 static,变为静态字段后,初始化是在类加载的时候,早于 nacos 的创建实例阶段,所以我们的字段跟着类加载的时候已经有了默认值,后面 nacos 的值自然赋值不上,除非我们显示的修改,那么我们要如何获取到 nacos 的值呢?

image-20230525213735228

首先先来看一下实体类,与之前唯一不同的地方就是我们只需要手动去创建字段的 get 和 set 方法,注意这里使用 idea 的快捷生成会在方法顺带加上 static 关键字,我们需要手动的把 get 和 set 方法上的 static 删掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "sora")
public class NacosVo implements Serializable {

@Serial
private static final long serialVersionUID = 8996476309916434072L;

public static String name;
public static String age;
public static String sex;


public void setName(String name) {
NacosVo.name = name;
}

public void setAge(String age) {
NacosVo.age = age;
}

public void setSex(String sex) {
NacosVo.sex = sex;
}

public String getName() {
return name;
}

public String getAge() {
return age;
}

public String getSex() {
return sex;
}
}

同时在我们需要使用这个对象的类里,引入 NacosVo 这个对象并初始化。

1
2
3
4
5
private static NacosVo nacosVo;

public AuthController(NacosVo nacosVo) {
AuthController.nacosVo = nacosVo;
}

启动程序,我们可以看到成功获取到了 nacos 中的值,我们尝试改变一下数据发现仍然可以获取到最新的记录,将 nacos 的属性信息赋值给静态字段并实现动态刷新就完成了

image-20230527215001956

原理剖析

我们来看这样一组实例

image-20230527221748344

在程序刚启动后,我分别通过 2 种方式获取 nacos 的配置,打印出的结果是一样的,下面我改变 nacos 的值并重新获取,可以看到,通过 get 方法获取的值是最新的,但是类名获取的仍然是第一次 nacos 的值。

image-20230527221902926

为什么会发生这种情况呢?首先我们需要知道 @RefreshScope 这个注解的作用,这个注解是标记一个 Bean 或者类,在配置发生变化的时候,Spring 会重新创建该容器。而 nacos 也有一个监听类,叫做 NacosConfigProperties,是用来监听 nacos 配置发生变化的,这两个组件的作用下,该 bean 会重新创建。所以后续通过 get 方法获取到的都是从最新的该 bean 中拿到的数据,而类名获取到的则是我们启动框架的时候,初始化进去的当前 nacos 值,同时因为是 static 修饰的,跟着类一起加载,如果后续我们没有进行显示的更改,那么是不会发生变化的。

最后总结一下,NacosConfigProperties 是负责监听 Nacos 配置变化的组件,而 RefreshScope 是负责动态刷新 Bean 的组件。它们一起工作,使得应用程序能够在配置发生变化时自动更新,并实现动态刷新 Bean 的属性值。