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

Spring自定义定时刷新Scope的get方法仅启动调用一次问题咨询

问题根因

你的自定义Scope本身实现逻辑没有问题,get方法只调用一次是Spring单例Bean的注入特性导致的:

你将自定义Scope的Bean直接注入到了单例的Controller中:Spring在启动初始化Controller时,会调用一次你的Scope的get方法拿到Bean实例,直接赋值给Controller的dateString字段,后续Controller使用的永远是首次注入的这个实例,自然不会再触发Scope的get逻辑。

解决方案

有两种常用的适配方案:

方案1:给自定义Scope添加动态代理

在声明Bean的@Scope注解中指定代理模式,让Spring注入代理对象,每次调用Bean方法时自动去Scope中拉取最新实例:

@Bean
@Scope(scopeName = "CyberArk", proxyMode = ScopedProxyMode.TARGET_CLASS)
public String dateString(){
    return LocalDateTime.now().toString();
}
  • 如果你的Bean是接口实现类,可使用ScopedProxyMode.INTERFACES代理模式,性能更优。

方案2:注入ObjectProvider延迟获取

不在初始化阶段就注入固定实例,而是注入对象提供者,每次需要使用时主动拉取最新实例:

@RestController
public class HelloWorld {
    @Autowired
    private ObjectProvider<String> dateStringProvider;

    @RequestMapping("/")
    public String index() {
        // 每次调用都会触发自定义Scope的get方法
        return dateStringProvider.getIfAvailable();
    }
}

现有实现优化建议

你当前的Scope实现在实例过期时直接覆盖了Map中的旧实例,没有触发对应的销毁回调,建议修改get方法的过期逻辑,先处理旧实例的销毁:

@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
    if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
        .isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
        // 先触发旧实例的销毁回调
        if (destructionCallbacks.containsKey(name)) {
            destructionCallbacks.get(name).run();
            destructionCallbacks.remove(name);
        }
        scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
    }
    return scopedObjects.get(name).getValue();
}

内容的提问来源于stack exchange,提问作者Evan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 12:36:08