聊聊使用@RefreshScope與nacos2整合踩到的坑

linyb極客之路發表於2022-11-23

前言

本文的素材來源於朋友整合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搭建過程中,也踩到了一些坑。感興趣的朋友可以檢視如下文章

記一次使用nacos2踩到的坑

相關文章