如何通过数据库动态配置application.properties实现产品属性实时获取?
动态从数据库加载产品属性的解决方案
一、自定义配置源(Spring Boot 环境)
如果是基于Spring Boot的项目,可通过实现PropertySource将数据库属性整合到Spring配置体系,支持运行时读取与动态刷新。
1. 实现自定义PropertySource
- 先定义对应数据库表的实体类(示例表字段:
product_code,config_key,config_value):
public class ProductConfig { private String productCode; private String configKey; private String configValue; // getter、setter略 }
- 实现
PropertySource,重写属性查询逻辑:
public class DbPropertySource extends PropertySource<JdbcTemplate> { private static final String NAME = "DbPropertySource"; private final JdbcTemplate jdbcTemplate; public DbPropertySource(JdbcTemplate jdbcTemplate) { super(NAME, jdbcTemplate); this.jdbcTemplate = jdbcTemplate; } @Override public Object getProperty(String name) { // 约定属性名格式为 product.{产品编码}.{配置键},比如 product.pay.maxAmount if (name.startsWith("product.")) { String[] parts = name.split("\\.", 3); if (parts.length == 3) { String productCode = parts[1]; String key = parts[2]; String sql = "SELECT config_value FROM product_config WHERE product_code = ? AND config_key = ?"; try { return jdbcTemplate.queryForObject(sql, new Object[]{productCode, key}, String.class); } catch (EmptyResultDataAccessException e) { return null; // 无匹配属性时返回null,不影响原有配置逻辑 } } } return null; } }
- 通过
EnvironmentPostProcessor将自定义源注册到Spring环境:
public class DbPropertySourceProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 直接从环境获取数据源,避免循环依赖 DataSource dataSource = environment.getRequiredProperty("spring.datasource", DataSource.class); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); DbPropertySource dbPropertySource = new DbPropertySource(jdbcTemplate); // 优先使用数据库配置,将其添加到属性源最前面 environment.getPropertySources().addFirst(dbPropertySource); } }
- 在
src/main/resources/META-INF/spring.factories中注册处理器:
org.springframework.boot.env.EnvironmentPostProcessor=com.yourpackage.DbPropertySourceProcessor
2. 支持动态刷新
若需修改数据库配置后实时生效,结合@RefreshScope与刷新接口实现:
@RestController public class ConfigRefreshController { private final ConfigurableEnvironment environment; private final JdbcTemplate jdbcTemplate; private final ContextRefresher contextRefresher; public ConfigRefreshController(ConfigurableEnvironment environment, JdbcTemplate jdbcTemplate, ContextRefresher contextRefresher) { this.environment = environment; this.jdbcTemplate = jdbcTemplate; this.contextRefresher = contextRefresher; } @PostMapping("/refresh-product-config") public String refresh() { // 移除旧的数据库属性源,添加新实例 MutablePropertySources propertySources = environment.getPropertySources(); propertySources.remove(DbPropertySource.NAME); propertySources.addFirst(new DbPropertySource(jdbcTemplate)); // 触发RefreshScope注解的Bean刷新 contextRefresher.refresh(); return "产品配置已刷新"; } }
注意:需引入spring-boot-starter-actuator依赖,并在配置中开启refresh端点。
二、轻量配置服务封装(非Spring生态/简单场景)
如果项目不依赖Spring体系,可自行封装配置服务类,结合缓存+定时刷新实现动态读取:
public class ProductConfigService { private static ProductConfigService instance; private final Map<String, Map<String, String>> productConfigCache = new ConcurrentHashMap<>(); private final JdbcTemplate jdbcTemplate; private static final long REFRESH_INTERVAL = 5 * 60 * 1000; // 5分钟刷新一次缓存 private ProductConfigService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; loadConfigToCache(); startRefreshTask(); } public static synchronized ProductConfigService getInstance(JdbcTemplate jdbcTemplate) { if (instance == null) { instance = new ProductConfigService(jdbcTemplate); } return instance; } private void loadConfigToCache() { String sql = "SELECT product_code, config_key, config_value FROM product_config"; List<ProductConfig> configs = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(ProductConfig.class)); productConfigCache.clear(); for (ProductConfig config : configs) { productConfigCache.computeIfAbsent(config.getProductCode(), k -> new ConcurrentHashMap<>()) .put(config.getConfigKey(), config.getConfigValue()); } } private void startRefreshTask() { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r); t.setDaemon(true); return t; }); executor.scheduleAtFixedRate(this::loadConfigToCache, REFRESH_INTERVAL, REFRESH_INTERVAL, TimeUnit.MILLISECONDS); } public String getConfig(String productCode, String key) { return productConfigCache.getOrDefault(productCode, Collections.emptyMap()).get(key); } }
业务代码中直接调用ProductConfigService.getInstance(jdbcTemplate).getConfig("pay", "maxAmount")即可获取最新配置,无需重启服务。
三、关键注意事项
- 数据库表优化:添加
update_time字段,刷新时仅查询更新时间晚于上次刷新时间的记录,提升性能 - 容错机制:数据库查询失败时保留旧缓存,避免服务不可用
- 实时性需求:若需配置变更立即生效,可通过数据库触发器、消息队列(如RabbitMQ)发送变更通知,触发缓存刷新
内容的提问来源于stack exchange,提问作者dhruv agarwal
相关产品推荐
相关产品推荐

