SpringBoot:如何让@Configuration的@Bean方法每次调用都重新执行?
你的问题根源在于Spring默认将Map<Long,Long>作为单例Bean管理,只会在容器启动时调用一次provideTierConfigurations方法初始化,后续即使TierDAL中的Map更新了,TierCalculator持有的还是初始化时的旧副本。以下是几种可行的解决方式:
方案一:直接注入TierDAL,按需获取最新配置
这是最简单直接的方式,跳过将Map作为Bean的步骤,让TierCalculator直接调用TierDAL的方法获取最新配置:
@Component public class TierCalculator { private final TierDAL tierDAL; // 推荐构造注入而非Setter注入 public TierCalculator(TierDAL tierDAL) { this.tierDAL = tierDAL; } // 每次需要配置时,直接获取最新副本 public void calculateTier(Long userId) { Map<Long, Long> latestTierConfig = tierDAL.getTierConfigurations(); // 使用最新配置进行计算逻辑 } }
这种方式的优势是避免Spring Bean作用域的复杂度,只要TierDAL#getTierConfigurations能返回最新数据,TierCalculator就能拿到最新配置。如果DAL内部已经做了缓存(比如本地缓存或分布式缓存),这种方式的性能开销可以忽略。
方案二:将Map Bean改为原型作用域,用Provider按需获取
如果必须通过Spring Bean来提供配置,可以将Map的作用域改为原型(prototype),这样每次从容器获取该Bean时,都会重新执行provideTierConfigurations方法生成新的实例。配合javax.inject.Provider(或Spring的ObjectProvider)来实现按需获取:
第一步:修改ConfigurationProvider的Bean作用域
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class ConfigurationProvider { @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 设置为原型作用域 Map<Long, Long> provideTierConfigurations(TierDAL dal) { return dal.getTierConfigurations(); } }
第二步:修改TierCalculator注入Provider
import javax.inject.Provider; import org.springframework.stereotype.Component; @Component public class TierCalculator { private final Provider<Map<Long, Long>> tierConfigProvider; public TierCalculator(Provider<Map<Long, Long>> tierConfigProvider) { this.tierConfigProvider = tierConfigProvider; } public void calculateTier(Long userId) { // 每次调用get()都会获取最新的Map实例 Map<Long, Long> latestTierConfig = tierConfigProvider.get(); // 执行计算逻辑 } }
注意:原型作用域的Bean每次获取都会生成新实例,这里因为是Map对象,创建和销毁的开销很小,所以可以放心使用。
方案三:定时刷新配置缓存(适合高开销场景)
如果TierDAL#getTierConfigurations的调用开销较大(比如需要远程调用数据库或第三方服务),可以实现一个定时刷新的缓存Holder,平衡实时性和性能:
第一步:创建配置Holder组件
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @Component public class TierConfigHolder { private final TierDAL tierDAL; private final AtomicReference<Map<Long, Long>> configCache; public TierConfigHolder(TierDAL tierDAL) { this.tierDAL = tierDAL; // 初始化缓存 this.configCache = new AtomicReference<>(tierDAL.getTierConfigurations()); } // 定时刷新缓存,比如每5分钟执行一次(单位:毫秒) @Scheduled(fixedRate = 300000) public void refreshConfig() { Map<Long, Long> newConfig = tierDAL.getTierConfigurations(); configCache.set(newConfig); } // 对外提供获取最新配置的方法 public Map<Long, Long> getLatestConfig() { return configCache.get(); } }
第二步:修改TierCalculator注入Holder
@Component public class TierCalculator { private final TierConfigHolder tierConfigHolder; public TierCalculator(TierConfigHolder tierConfigHolder) { this.tierConfigHolder = tierConfigHolder; } public void calculateTier(Long userId) { Map<Long, Long> latestTierConfig = tierConfigHolder.getLatestConfig(); // 执行计算逻辑 } }
这种方式需要开启Spring的定时任务支持,在配置类上添加@EnableScheduling注解即可。
内容的提问来源于stack exchange,提问作者Shvalb

