You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 23:05:31