Spring Boot中如何在EnvironmentChangeEvent内获取ConfigurationProperty实例?
解决Spring Boot Actuator重载后获取更新配置的问题
核心问题原因
默认情况下@ConfigurationProperties注解的Bean是单例,配置重载后Spring不会自动创建新实例,直接从上下文获取的还是旧对象;另外你的users字段是final,即使触发重新绑定也无法更新值。
解决方案步骤
1. 修改配置类,支持刷新
给AppConfigurationProperties添加@RefreshScope注解,并去掉字段的final修饰符,添加Setter方法(用Lombok的@Setter即可):
@ConfigurationProperties(prefix = "app") @RefreshScope // 启用刷新作用域,配置变更后生成新实例 @ToString @Getter @Setter // 允许Spring重新绑定属性值 public class AppConfigurationProperties { private List<String> users; // 移除final修饰符 }
2. 调整服务类,实现差异对比
在UserService中保存旧的配置值,监听EnvironmentChangeEvent时通过代理对象获取最新配置,然后对比差异执行操作:
@Service @Slf4j public class UserService { private final AppConfigurationProperties properties; private List<String> oldUsers; @Autowired public UserService(AppConfigurationProperties properties) { this.properties = properties; this.oldUsers = new ArrayList<>(properties.getUsers()); // 初始化订阅逻辑 subscribeNewUsers(oldUsers); } @Bean public ApplicationListener<EnvironmentChangeEvent> environmentChangeListener() { return event -> { List<String> newUsers = properties.getUsers(); if (!newUsers.equals(oldUsers)) { // 计算新增用户 List<String> addedUsers = newUsers.stream() .filter(user -> !oldUsers.contains(user)) .collect(Collectors.toList()); // 计算移除用户 List<String> removedUsers = oldUsers.stream() .filter(user -> !newUsers.contains(user)) .collect(Collectors.toList()); // 执行订阅/取消订阅操作 subscribeNewUsers(addedUsers); unsubscribeUsers(removedUsers); // 更新旧列表 oldUsers = new ArrayList<>(newUsers); } }; } private void subscribeNewUsers(List<String> users) { // 替换为你的WebSocket订阅逻辑 users.forEach(user -> log.info("订阅用户: {}", user)); } private void unsubscribeUsers(List<String> users) { // 替换为你的WebSocket取消订阅逻辑 users.forEach(user -> log.info("取消订阅用户: {}", user)); } }
3. 启用Actuator Refresh端点
在application.yaml中配置开启refresh端点,允许触发配置重载:
management: endpoints: web: exposure: include: refresh endpoint: refresh: enabled: true
备选方案:手动绑定配置(无需@RefreshScope)
如果不想用@RefreshScope,可以通过ConfigurationPropertiesBindingPostProcessor手动创建并绑定新的配置实例:
@Service @Slf4j public class UserService { private final ConfigurationPropertiesBindingPostProcessor binder; private List<String> oldUsers; @Autowired public UserService(ConfigurationPropertiesBindingPostProcessor binder) { this.binder = binder; // 初始化旧配置 AppConfigurationProperties initialProps = new AppConfigurationProperties(); binder.postProcessBeforeInitialization(initialProps, "app"); this.oldUsers = new ArrayList<>(initialProps.getUsers()); subscribeNewUsers(oldUsers); } @Bean public ApplicationListener<EnvironmentChangeEvent> environmentChangeListener() { return event -> { // 创建新实例并绑定最新配置 AppConfigurationProperties newProps = new AppConfigurationProperties(); binder.postProcessBeforeInitialization(newProps, "app"); List<String> newUsers = newProps.getUsers(); if (!newUsers.equals(oldUsers)) { // 对比差异并执行操作 List<String> addedUsers = newUsers.stream().filter(u -> !oldUsers.contains(u)).toList(); List<String> removedUsers = oldUsers.stream().filter(u -> !newUsers.contains(u)).toList(); subscribeNewUsers(addedUsers); unsubscribeUsers(removedUsers); oldUsers = new ArrayList<>(newUsers); } }; } // 订阅/取消订阅方法同上 }
内容的提问来源于stack exchange,提问作者tftd
相关产品推荐
相关产品推荐

