前言
本文的素材來源於朋友整合nacos2作為配置中心進行動態重新整理時,踩到的坑。他當時遇到的問題,如下截圖
因為那段時間比較忙,於是我在沒看朋友專案程式碼的基礎上,就找個了看似解決方案的答案,扔了過去
後面朋友加了這個配置,問題果然沒有解決。後面就抽了一點時間,要了他的專案程式碼來看下。
程式碼示例
因為他這個專案主要是他自學nacos的專案,也沒涉及啥敏感資訊。本文就直接拿他的專案示例演示
1、專案pom依賴
<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>
注: nacos服務端版本為2.1.1
2、nacos配置中心地址,配置在bootstrap.yml裡面
spring:
cloud:
nacos:
server-addr: localhost:8848
3、專案的基本資訊配置在application.yml裡面
#
spring:
application:
name: nacos-config
server:
port: 8030
4、編寫一個需要動態重新整理獲取值的controller
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${user.userName:123}")
private String userName;
@RequestMapping("/get")
private String get(){
return userName;
}
}
5、業務專案在nacos服務端上配置如下
以上就是朋友完整的程式碼例子。我們執行一下程式碼,會發現controller的userName取不到值。感興趣的朋友,可以走查一下上述的程式碼,查詢一下原因
取不到值的原因
理論知識鋪墊:
當我們使用cglib動態代理呼叫目標方法時,當方法被private修飾時,this為動態代理物件。當方法被public或者protected修飾時,this為目標物件。此外屬性重新整理刷的是目標物件的屬性,controller的get方法可以看成是
@RequestMapping("/get")
private String get(){
return this.userName;
}
當我們在controller加上@RefreshScope註解時,如果不改變變proxyMode這個屬性值時,他預設就會生成一個cglib動態代理。當我們呼叫get方法,因為get為私有方法,我們可以看成
@RequestMapping("/get")
private String get(){
return cglibProxy.userName;
}
此時的this是代理物件,而此時userName是代理物件的userName,代理物件的userName是空值。
解決方法
方法一、修改@RefreshScope的proxyMode屬性
將proxyMode改為ScopedProxyMode.DEFAULT或者ScopedProxyMode.NO
此時this為目標物件,因此能取到值
方法二:將目標方法的修飾符改為public或者protected
示例:
此時this為目標物件,因此能取到值
3、方法三:使用屬性配置類
@Configuration
@ConfigurationProperties(prefix = "user")
public class UserProperties {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
controller調整成
@RestController
@RequestMapping("/config")
public class ConfigController {
@Autowired
private UserProperties userProperties;
@RequestMapping("/get")
public String get(){
return userProperties.getUserName();
}
}
此時controller不用加@RefreshScope也能實現動態重新整理。因為屬性類上的@ConfigurationProperties本身就具有動態重新整理的特性
總結
本文不算是@RefreshScope與nacos2整合踩到的坑,主要還是動態代理方面的知識,題目有點標題黨了。
有些影片講nacos動態重新整理時,基本上都是舉controller上@RerfreshScope +@value來講解。其實利用@ConfigurationProperties也是可以達到類似的效果。如果沒和springcloud整合,引入nacos配置中心的starter,使用@NacosPropertySource + @NacosValue或者@NacosRefresh也是可以實現動態重新整理,感興趣的朋友可以試一下
最後,朋友之前在nacos2搭建過程中,也踩到了一些坑。感興趣的朋友可以檢視如下文章