Spring Boot中基于@PostConstruct的缓存如何实现定期自动刷新?
原生Java/Spring实现缓存定期自动刷新方案
针对你的场景,以下两种基于原生Java/Spring类库的方案可以实现缓存的定期自动刷新,无需依赖第三方缓存框架:
方案一:使用Spring @Scheduled注解(推荐,贴合Spring生态)
这是Spring原生支持的定时任务方案,配置简单,无需额外依赖。
实现步骤:
- 在Spring Boot启动类或配置类上添加
@EnableScheduling,开启定时任务功能:
@SpringBootApplication @EnableScheduling public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
- 重构缓存类,将初始化逻辑抽为公共刷新方法,通过
@PostConstruct初始化缓存,同时用@Scheduled配置定时刷新:
@Component public class ConfigCache { private final MyRepository myRepository; // volatile保证多线程下缓存替换的可见性 private volatile ConcurrentHashMap<String, String> cacheMap = new ConcurrentHashMap<>(); // 构造注入Repository public ConfigCache(MyRepository myRepository) { this.myRepository = myRepository; } @PostConstruct public void initCache() { refreshCache(); } // 每30分钟刷新一次(cron表达式:0秒、每30分钟、任意小时/日期/月份/星期) @Scheduled(cron = "0 0/30 * * * ?") public void refreshCache() { // 先加载全量数据到临时Map,避免刷新过程中缓存不一致 ConcurrentHashMap<String, String> newCache = new ConcurrentHashMap<>(); List<MyEntity> configList = myRepository.findAll(); for (MyEntity entity : configList) { newCache.put(entity.getKey(), entity.getValue()); } // 原子替换缓存Map,保证线程安全 this.cacheMap = newCache; } // 对外提供的缓存查询方法 public String getConfig(String key) { return cacheMap.get(key); } }
方案二:使用Java原生ScheduledExecutorService(纯Java实现,不依赖Spring定时注解)
如果不想依赖Spring的定时注解,可以用Java自带的线程池调度器实现定时任务。
@Component public class ConfigCache { private final MyRepository myRepository; private volatile ConcurrentHashMap<String, String> cacheMap = new ConcurrentHashMap<>(); private ScheduledExecutorService scheduler; public ConfigCache(MyRepository myRepository) { this.myRepository = myRepository; } @PostConstruct public void initCache() { refreshCache(); // 初始化单线程调度器,首次延迟30分钟,之后每30分钟执行一次刷新 scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(this::refreshCache, 30, 30, TimeUnit.MINUTES); } // 应用关闭时销毁调度器,避免资源泄漏 @PreDestroy public void shutdownScheduler() { if (scheduler != null) { scheduler.shutdown(); try { if (!scheduler.awaitTermination(1, TimeUnit.MINUTES)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } } } private void refreshCache() { ConcurrentHashMap<String, String> newCache = new ConcurrentHashMap<>(); List<MyEntity> configList = myRepository.findAll(); for (MyEntity entity : configList) { newCache.put(entity.getKey(), entity.getValue()); } this.cacheMap = newCache; } public String getConfig(String key) { return cacheMap.get(key); } }
关键注意事项
- 线程安全:用
volatile修饰缓存Map,保证多线程下的可见性;刷新时先加载数据到临时Map再替换原Map,避免部分更新导致的缓存不一致。 - 性能优化:如果数据库配置数据量较大,建议分批查询加载,避免单次查询耗时过长阻塞应用。
- 异常处理:可以在
refreshCache方法中添加异常捕获与日志记录,必要时实现重试逻辑,避免单次刷新失败导致缓存长期失效。 - 任务调度线程:使用
@Scheduled时,默认是单线程调度,若存在多个定时任务,可配置TaskScheduler使用线程池,防止任务阻塞。
内容的提问来源于stack exchange,提问作者Pranjal Sharma
相关产品推荐
相关产品推荐

