背景
springboot單體項目在運行過程需要刷新springboot配置文件值,比如某個接口限流閾值,新增某個賬戶等場景。分布式設計的可以直接引入一些持久化中間件比如redis等,也可以用相關配置中心中間件如nacos等。處于成本等場景單體項目可以考慮①把配置文件緩存到java對象中,然后暴露出刷新配置文件接口②本文主要講如何利用spring-cloud-context組件快速實現手動更新配置文件。
原理
依賴
注意不同springboot版本和springcloud要對應https://spring.io/projects/spring-cloud#overview
pom.xml
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version></properties><!--注意不同springboot版本和cloud要對應https://spring.io/projects/spring-cloud#overview--><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2021.0.3</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-context</artifactId></dependency></dependencies>
配置文件
application.properties
server.refresh=${random.long}
server.key=${random.long}
config.uuid=${random.uuid}
代碼
演示@ConfigurationProperties和@value方式引入的兩種方式讀取配置文件的刷新
注意@value方式引入需要在對應類上添加@RefreshScope注解
ServerConfig.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "server")
@Data
public class ServerConfig {private String key;private Long refresh;
}
ValueConfig.java
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;@RefreshScope// @Value這種方式必須標注此注解contextRefresher.refresh()后才會生效
@Component
@Data
public class ValueConfig {@Value("${config.uuid}")private String uuid;
}
測試接口DemoController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Set;@RestController
public class DemoController {@Autowiredprivate ContextRefresher contextRefresher;@Autowiredprivate ServerConfig serverConfig;@Autowiredprivate ValueConfig valueConfig;@GetMapping(path = "/show")public Object show() {// 取得的值不變return "serverConfig:"+serverConfig+" valueConfig:"+valueConfig;}@GetMapping(path = "/refresh")public Object refresh() {// 刷新后將獲取到刷新后的值 ,包括新增key,注意開發模式運行服務后編譯后的文件路徑,一般是在targetnew Thread(() -> {contextRefresher.refresh();}).start();return show();}
}
測試
訪問/refresh接口可以看到每次獲取的值不一樣,當然也可以手動更新配置文件的值(更新v、新增k-v、刪除k-v),注意開發模式手動更改是在編譯后的文件路徑,一般是在target下
serverConfig:ServerConfig(key=8733164433754704077, refresh=5118825739210703127) valueConfig:ValueConfig(uuid=ca7bceba-66ef-4ea4-a4f8-6caa027927f3)
拓展
如果想要配置文件更新后做一些其它動作,比如郵件通知下牛馬開發員,從可以從contextRefresher.refresh()源碼得知配置文件發送變更后會觸發this.context.publishEvent,利用spring的事件通知機制就可以實現了。了解更多@EventListener注解知識可以參考博客[https://blog.csdn.net/u012060033/article/details/136006082]
@Configurationpublic class EnvConfiguration{@EventListenerpublic void envListener(EnvironmentChangeEvent event) {// 配置文件刷新會觸發此事件,key的變化可以通過event.getKeys()獲取System.out.println("conf change: " + event);}}