面试时候,基础的知识还是要好好整理的,今天有一个兄弟就跟我说他遇到了一个问SB动态改配置的方法有哪些,我整理了下,大概就这些:
在开发过程中,配置的动态修改几乎是每个项目都会遇到的需求。有时候,产品会在上线后要求动态调整参数,而不能重启服务。为了应对这些需求,我总结了 12 种 SpringBoot 动态改配置的方法,从简单的本地实现到复杂的中心化配置,总有一种适合你的项目。
@Value
+ 环境监听器:简单实用的本地方案@Value
注解虽然简单,但默认修改配置后需要重启服务。通过结合环境监听器,可以实现动态修改.
@Component
public class TimeoutConfigListener implements ApplicationListener {
@Value("${timeout:30}")
private String timeout;
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
if (event.getKeys().contains("timeout")) {
String newVal = ApplicationContextProvider.getContext()
.getEnvironment()
.getProperty("timeout");
timeout = newVal;
System.out.println("超时时间改了:" + timeout);
}
}
}
同时,需要一个 ApplicationContextProvider
来获取上下文:
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
public static ApplicationContext getContext() {
return ctx;
}
}
这种方法简单直接,适合小型项目。
Nacos 是一个常用的中心化配置管理工具,结合 Spring Cloud 可以轻松实现配置的动态刷新.
@RefreshScope
@RestController
public class BizController {
@Value("${biz.feature.switch:false}")
private boolean featureSwitch;
@GetMapping("/feature/status")
public String checkFeature() {
return "当前功能开关状态:" + featureSwitch;
}
}
在 bootstrap.yml
中配置 Nacos:
spring:
application:
name: config-demo
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
修改 Nacos 中的配置后,服务会自动刷新,无需重启。
Apollo 是一个功能强大的配置管理系统,支持灰度发布、审核等高级功能.
@RestController
public class ApolloController {
@ApolloConfig
private Config config;
@GetMapping("/current/env")
public String getEnv() {
String env = config.getProperty("app.env", "dev");
return "当前环境是:" + env;
}
}
Apollo 不依赖 Spring 的缓存机制,修改配置后会立即生效。
在多数据源场景下,可以通过 AbstractRoutingDataSource
和 ThreadLocal
动态切换数据源。
核心代码如下:
public class DynamicDataSourceContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
public static void setDataSource(String key) {
contextHolder.set(key);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSource();
}
}
在 Controller 中切换数据源:
DynamicDataSourceContextHolder.setDataSource("tenantA");
// 查询数据库
DynamicDataSourceContextHolder.clearDataSource();
这种方法可以灵活切换数据源,无需重启服务。
通过反射或从 Spring 容器中获取 Bean,可以直接修改其属性.
@Service
public class BizService {
private int retryCount = 3;
public void doSomething() {
System.out.println("当前重试次数:" + retryCount);
}
public void setRetryCount(int retryCount) {
this.retryCount = retryCount;
}
}
通过接口动态修改属性:
@Autowired
private ApplicationContext ctx;
@PostMapping("/set-retry")
public String setRetry(@RequestParam int count) {
BizService bean = ctx.getBean(BizService.class);
bean.setRetryCount(count);
return "已设置重试次数为:" + count;
}
这种方法适合需要动态调整服务参数的场景。
通过内存 Map 存储配置,结合数据库或 Redis,可以快速实现动态配置.
@Component
public class InMemoryConfig {
private final Map configMap = new ConcurrentHashMap<>();
public String get(String key) {
return configMap.getOrDefault(key, "");
}
public void set(String key, String val) {
configMap.put(key, val);
}
}
提供接口操作配置:
@Autowired
private InMemoryConfig config;
@PostMapping("/update-config")
public String update(@RequestParam String key, @RequestParam String value) {
config.set(key, value);
return "更新成功";
}
@GetMapping("/get-config")
public String get(@RequestParam String key) {
return config.get(key);
}
这种方法适合小型项目,无需复杂的配置中心。
Spring Cloud Config 是一个强大的分布式配置管理工具,支持 Git 存储配置文件,并且可以动态刷新。
服务端配置:
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/your-team/config-repo.git
客户端配置:
spring:
application:
name: your-app
cloud:
config:
uri: http://localhost:8888
客户端使用 @RefreshScope
动态刷新:
@RefreshScope
@RestController
public class ConfigClientController {
@Value("${biz.name:default}")
private String bizName;
@GetMapping("/get-biz")
public String biz() {
return "当前业务名:" + bizName;
}
}
修改 Git 配置后,调用 /actuator/refresh
即可刷新配置。
通过定时任务定期从 Redis 或数据库拉取配置,更新到内存中.
@Component
public class ConfigPuller {
private final Map localCache = new ConcurrentHashMap<>();
@Scheduled(fixedRate = 10000)
public void refreshConfig() {
String val = redisTemplate.opsForValue().get("sys.timeout");
if (val != null) {
localCache.put("sys.timeout", val);
System.out.println("刷新配置,当前timeout:" + val);
}
}
public String get(String key) {
return localCache.getOrDefault(key, "");
}
}
这种方法简单直接,适合无法使用配置中心的场景。
通过发布事件通知各模块更新配置。
定义事件类:
public class ConfigUpdateEvent extends ApplicationEvent {
private final String key;
private final String value;
public ConfigUpdateEvent(Object source, String key, String value) {
super(source);
this.key = key;
this.value = value;
}
// get方法略
}
监听事件:
@Component
public class BizModule implements ApplicationListener {
@Override
public void onApplicationEvent(ConfigUpdateEvent event) {
if ("biz.switch".equals(event.getKey())) {
System.out.println("收到配置更新事件,biz.switch 改成了:" + event.getValue());
}
}
}
发布事件:
@Autowired
private ApplicationEventPublisher publisher;
@PostMapping("/update")
public String update(@RequestParam String key, @RequestParam String val) {
publisher.publishEvent(new ConfigUpdateEvent(this, key, val));
return "配置发布成功";
}
这种方法适合需要多模块协作的场景。
Environment
动态修改配置通过操作 Spring 的 Environment
,可以直接修改配置.
@Autowired
private ConfigurableEnvironment environment;
public void updateProperty(String key, String value) {
MutablePropertySources propSources = environment.getPropertySources();
Map map = new HashMap<>();
map.put(key, value);
propSources.addFirst(new MapPropertySource("customOverride", map));
}
这种方法适合临时修改配置的场景,重启后会失效。
@ConfigurationProperties
+ 动态修改通过 @ConfigurationProperties
和 @RefreshScope
,可以动态修改配置类的属性。
配置类:
@Component
@ConfigurationProperties(prefix = "biz.config")
@RefreshScope
public class BizProperties {
private int threadCount;
private boolean enable;
// getter/setter...
}
动态修改:
@Autowired
private BizProperties props;
@PostMapping("/set-thread")
public String update(@RequestParam int count) {
props.setThreadCount(count);
return "线程数改成:" + count;
}
整体来说,大致就这几种可以动态修改springboot的值。
更多精彩,请移步 :codingba 或识别如下二维码,更多咨询等着你。