如何重新初始化标注@Document的Spring Bean并自动同步集合更新
实现MongoDB配置变更时自动更新Spring注入的ConfigData Bean
我有一个标注了@Document的Spring Bean(ConfigData类),已经通过@Bean配置使其可以通过@Autowired注入。现在希望当MongoDB集合中的数据发生变化时,@Autowired注入的ConfigData实例能自动获取这些变更,请问该如何实现?
ConfigData类代码
@Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString @Document(collection = CollectionNames.CONFIG_DATA) public class ConfigData { @Id private String id; private Profiles profiles; private EmailConfig emailConfig; private ScheduledConfig scheduledConfig; @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public static class EmailConfig { private String email; private String password; } @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public static class ScheduledConfig { private Long time; private TimeUnit timeUnit; public long convertToMilliseconds(long value, TimeUnit unit) { return switch (unit) { case HOURS -> java.util.concurrent.TimeUnit.HOURS.toMillis(value); case MINUTES -> java.util.concurrent.TimeUnit.MINUTES.toMillis(value); case SECONDS -> java.util.concurrent.TimeUnit.SECONDS.toMillis(value); }; } public enum TimeUnit { HOURS, MINUTES, SECONDS } } }
配置类代码
@Configuration public class MongoConfig { @Autowired private ConfigDataService configDataService; @Autowired private Environment environment; @Bean public ConfigData configData() { return configDataService.getConfigDataByProfile(Profiles.valueOf(environment.getActiveProfiles()[0].toUpperCase(Locale.ROOT))); } }
尝试过的无效方法
- 使用
DefaultSingletonBeanRegistry的destroySingleton和registerSingleton方法销毁并重新注册Bean,执行报错; - 添加定时任务尝试更新
ConfigData,未生效,定时任务代码如下:
@Service public class TestOne { @Autowired private MongoConfig mongoConfig; @Autowired private ConfigData configData; @Scheduled(fixedRate = 1000) public void test() { ConfigData configData1 = mongoConfig.configData(); configData.setScheduledConfig(configData1.getScheduledConfig()); } }
可行解决方案
方法一:利用MongoDB Change Stream实时监听变更
Spring Data MongoDB支持监听MongoDB的Change Stream,当目标集合数据发生增删改时会触发事件,可通过该特性实时更新Bean内容。
- 确保Spring Data MongoDB版本在2.2+(Spring Boot 2.2+默认满足);
- 创建监听类,通过
@DocumentListener处理变更事件:
@Component public class ConfigDataChangeListener { private final ConfigData configData; private final ConfigDataService configDataService; private final Environment environment; public ConfigDataChangeListener(ConfigData configData, ConfigDataService configDataService, Environment environment) { this.configData = configData; this.configDataService = configDataService; this.environment = environment; } @DocumentListener public void handleConfigUpdate(AfterSaveEvent<ConfigData> event) { ConfigData updatedDoc = event.getSource(); Profiles activeProfile = Profiles.valueOf(environment.getActiveProfiles()[0].toUpperCase(Locale.ROOT)); // 仅更新当前激活环境对应的配置 if (activeProfile.equals(updatedDoc.getProfiles())) { configData.setEmailConfig(updatedDoc.getEmailConfig()); configData.setScheduledConfig(updatedDoc.getScheduledConfig()); // 其他字段按需同步 } } }
该方式无需轮询,仅在数据变更时触发更新,实时性强。
方法二:优化定时任务方案
之前的定时任务未生效,大概率是缺少@EnableScheduling注解,或逻辑存在疏漏,优化后的代码如下:
- 在启动类添加
@EnableScheduling启用定时任务; - 修改定时任务类:
@Service public class ConfigDataRefreshTask { private final ConfigData configData; private final ConfigDataService configDataService; private final Environment environment; public ConfigDataRefreshTask(ConfigData configData, ConfigDataService configDataService, Environment environment) { this.configData = configData; this.configDataService = configDataService; this.environment = environment; } @Scheduled(fixedRate = 5000) // 根据需求调整轮询间隔 public void refreshConfig() { Profiles activeProfile = Profiles.valueOf(environment.getActiveProfiles()[0].toUpperCase(Locale.ROOT)); ConfigData latestData = configDataService.getConfigDataByProfile(activeProfile); // 同步最新配置到已注入的Bean实例 configData.setEmailConfig(latestData.getEmailConfig()); configData.setScheduledConfig(latestData.getScheduledConfig()); } }
方法三:分布式场景用Spring Cloud Config(可选)
如果是多服务分布式架构,可搭建Spring Cloud Config配置中心,配合Spring Cloud Bus实现配置的集中管理和全局自动刷新,适合多服务共享配置的场景。
关键注意事项
- 禁止手动销毁/重新注册Singleton Bean:Spring容器会管理Singleton Bean的生命周期,手动操作会破坏容器依赖关系,导致异常;
- 直接修改现有Bean属性:Singleton Bean是全局共享实例,修改其属性后,所有注入该Bean的地方都会获取到最新值;
- 保证线程安全:若存在多线程读写配置的场景,需给
ConfigData的set方法添加synchronized,或使用线程安全的存储结构。
内容的提问来源于stack exchange,提问作者YourDaddyProgrammer
相关产品推荐
相关产品推荐

