Cache Preheating in Spring: Abstract Cache, Context Utility, and Implementation
This article explains the concept of cache preheating, provides an abstract cache class, a Spring ApplicationContext utility, a CommandLineRunner handler to load hot data at startup, and demonstrates a concrete NewsCache implementation with Redis integration, followed by a REST endpoint to access the cached data.
In this tutorial the author, a senior architect, introduces the concept of cache preheating, which proactively loads hot data into the cache after application start or cache expiration to reduce cache penetration, cache breakdown, and database pressure.
The core idea is implemented through an abstract cache class that defines the lifecycle methods init() , get() , clear() , and a default reload() that clears and re‑initialises the cache.
public abstract class AbstractCache {
protected abstract void init();
public abstract
T get();
public abstract void clear();
public void reload() {
clear();
init();
}
}A Spring ApplicationContextUtil is provided to expose the ApplicationContext as a static bean, allowing other components to retrieve beans programmatically.
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getContext() {
return applicationContext;
}
}The CachePreheatHandler implements CommandLineRunner and runs after the application starts. It obtains all beans that extend AbstractCache via ApplicationContextUtil and calls their init() method to fill the cache.
@Component
@ConditionalOnProperty(name = {"cache.init.enable"}, havingValue = "true", matchIfMissing = false)
public class CachePreheatHandler implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
ApplicationContext context = ApplicationContextUtil.getContext();
Map
beansOfType = context.getBeansOfType(AbstractCache.class);
for (Map.Entry
entry : beansOfType.entrySet()) {
AbstractCache cache = context.getBean(entry.getValue().getClass());
cache.init();
}
}
}As a concrete example, NewsCache extends AbstractCache . It uses a RedisTemplate to store a list of news items fetched from NewsService . The init() method populates Redis only if the key does not exist, and the get() method reloads the cache on miss.
@Component
@RequiredArgsConstructor
public class NewsCache extends AbstractCache {
private static final String NEWS_KEY = "news";
private final RedisTemplate
redisTemplate;
private final NewsService newsService;
@Override
protected void init() {
if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {
redisTemplate.opsForValue().set(NEWS_KEY, newsService.list(), 30, TimeUnit.MINUTES);
}
}
@Override
public
T get() {
if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {
reload();
}
return (T) redisTemplate.opsForValue().get(NEWS_KEY);
}
@Override
public void clear() {
redisTemplate.delete(NEWS_KEY);
}
}A simple NewsController exposes a /news/cache endpoint that returns the cached news list by delegating to NewsCache.get() .
@RestController
@RequestMapping("/news")
@RequiredArgsConstructor
public class NewsController {
private final NewsCache newsCache;
@GetMapping("/cache")
public List
list() {
return newsCache.get();
}
}After the technical guide, the article includes promotional material about ChatGPT services, a paid community, and various marketing links, which are not part of the technical content.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.