SpringBoot 動態加載配置文件及刷新Bean


需求

系統遇到這樣一個需求,線上環境在配置文件發生變動時,可以不用經過重啟,通過刷新接口的方式得到配置文件的加載,主要目的是為了迅速部署,避免因手動重啟,出現數據或任務丟失的問題

問題

1.程序中如何獲取修改后的配置
2.某些配置值是應用在bean里面的,在程序初始化的時候已經注入,如何修改這一部分。

解決

第一步,添加適合自己springboot版本的Springcloud context依賴,若Springboot版本低,maven可能會引不上高版本的context

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-context</artifactId>
    </dependency>
</dependencies>

我們這里參考 Spring-Cloud-Config 主要借助 org.springframework.cloud.context.refresh.ContextRefresher 這個類來實現配置刷新,因此需要對 SpringBoot 項目做一點修改。

代碼演示

配置文件: application.yml

server: 
refresh: ${random.long}
key: refresh-test

config: uuid: ${random.uuid}

讀取配置的Bean,兩種獲取方式分別如下

@Component
@ConfigurationProperties(prefix = "server")
public class ServerConfig {
    private String key;
    private Long refresh;
}

開啟刷新@Value的注解方式,注意下面的@RefreshScoe注解,這個必須有,否則更新后的配置不會同步

@RefreshScope
@Component
public class ValueConfig {
    @Value("${config.uuid}")
    private String uuid;
}

測試 Controller 如下

@RestController
public class DemoController {
    @Autowired
    private ContextRefresher contextRefresher;

    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    private ValueConfig valueConfig;

    @GetMapping(path = "/show")
    public String show() {
        JSONObject res = new JSONObject();
        res.put("biz", JSONObject.toJSONString(serverConfig));
        res.put("uuid", valueConfig.getUuid());
        return res.toJSONString();
    }

    @GetMapping(path = "/refresh")
    public String refresh() {
        new Thread(() -> contextRefresher.refresh()).start();
        return show();
    }
}

3. 實例演示

啟動上面的應用,然后開啟愉快的測試,調用refresh接口,發現每次的返回都不一樣(因為配置文件使用了random隨機生成),但是訪問show接口時,每次返回的都是一樣的,也就是說refresh接口中確實實現了配置的刷新。

說明

  • 使用ConfigurationProperties方式獲取注解時,自動支持刷新配置
  • 使用@Value注解的方式,需要開啟@RefreshScope注解(上面沒有演示不開啟這個注解的情況, 建議有興趣的可以自己嘗試一下)

II. 配置變更監聽

既然配置能刷新,那么如果我希望獲取配置變更的事件,然后做一些其他的事情,是否ok呢?

其實進入 ContextRefresher 的源碼,看下refresh接口,就很明確了

public synchronized Set<String> refresh() {
    Map<String, Object> before = extract(
            this.context.getEnvironment().getPropertySources());
    addConfigFilesToEnvironment();
    Set<String> keys = changes(before,
            extract(this.context.getEnvironment().getPropertySources())).keySet();
    // 注意這一行,拋出了一個變更事件
    this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
    this.scope.refreshAll();
    return keys;
}

1. 配置變更監聽

從上面的源碼中,借助spring的事件通知機制,很簡單就可以知道該怎么做了

import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;

@Configuration
public class EnvConfiguration{

    @EventListener
    public void envListener(EnvironmentChangeEvent event) {
        System.out.println("conf change: " + event);
    }

}

注意下控制台的輸出即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM