如何在单例Bean中刷新@RefreshScope Bean?BeanFactory.getBean无效
问题
运行时更新/刷新特定配置值时,已成功刷新配置(可通过actuator/env验证),但无法更新RefreshScope作用域下的配置Bean。调用刷新端点后,接口仍返回旧值,尝试调用refreshScope.refreshAll()、通过BeanFactory获取新实例均无效,仅直接从Environment获取属性有效,希望获取配置类的刷新后实例。
配置Bean代码:
@ConfigurationProperties(prefix = "refreshable") @RefreshScope public record RefreshableProperties( Optional<String> valueOne, Optional<Integer> valueTwo ) { }
控制器代码:
@RestController @RequiredArgsConstructor public class MainController { private final BeanFactory beanFactory; private final ContextRefresher contextRefresher; private final RefreshScope refreshScope; @GetMapping("/current") public String getCurrent() { return loadCurrent().toString(); } @GetMapping("/refresh") public String refresh() { contextRefresher.refresh();//此操作有效,actuator/env已返回刷新后的值 refreshScope.refreshAll(); return loadCurrent().toString(); // 返回旧值 } private RefreshableProperties loadCurrent() { return beanFactory.getBean(RefreshableProperties.class); } }
解决方案
问题核心是RefreshScope的代理机制和Bean获取方式不匹配,按以下方式调整即可:
直接注入
RefreshableProperties替代BeanFactory获取RefreshScope管理的Bean是代理对象,直接通过Spring依赖注入获取的代理实例,会在每次调用方法时自动检查缓存是否失效,若已失效则重新创建绑定最新配置的实例。修改控制器代码:@RestController @RequiredArgsConstructor public class MainController { private final ContextRefresher contextRefresher; private final RefreshScope refreshScope; // 直接注入RefreshableProperties private final RefreshableProperties refreshableProperties; @GetMapping("/current") public String getCurrent() { return refreshableProperties.toString(); } @GetMapping("/refresh") public String refresh() { contextRefresher.refresh(); refreshScope.refreshAll(); // 此时调用refreshableProperties会自动拿到新实例 return refreshableProperties.toString(); } }若必须手动获取Bean,通过
RefreshScope的get方法
若场景限制必须手动获取,不能直接用BeanFactory.getBean(),而是要通过RefreshScope的API获取最新实例:private RefreshableProperties loadCurrent() { return (RefreshableProperties) refreshScope.get( () -> beanFactory.getBean(RefreshableProperties.class) ); }核心逻辑说明
RefreshScope会为Bean生成代理,调用refreshAll()时会清除该作用域内的Bean缓存。直接注入的代理Bean每次调用方法时会校验缓存状态,而BeanFactory.getBean()拿到的是旧代理实例,不会主动触发缓存校验和实例重建。
内容的提问来源于stack exchange,提问作者dermoritz
相关产品推荐
相关产品推荐

