How to Implement Redis Cache Warming in Spring Boot

This article demonstrates how to implement Redis cache warming in Spring Boot using an abstract cache class, ApplicationContextAware utility, and CommandLineRunner to preload hot data at startup, with a concrete NewsCache example showing RedisTemplate integration and automatic cache reload on expiration.

IoT Full-Stack Technology
IoT Full-Stack Technology
IoT Full-Stack Technology
How to Implement Redis Cache Warming in Spring Boot

What Is Cache Warming?

Cache warming is a strategy that proactively loads hot data into the cache after application startup or cache invalidation. When real requests arrive, the hot data already exists in the cache, reducing cache penetration and cache breakdown while relieving pressure on the SQL server.

Implementation

Abstract Cache Class

First, define an abstract cache class. When a module needs to preload data into the cache, create an implementation of this class to handle caching and loading.

public abstract class AbstractCache {
    /** Cache */
    protected abstract void init();

    /** Get cache */
    public abstract <T> T get();

    /** Clear cache */
    public abstract void clear();

    /** Reload cache */
    public void reload() {
        clear();
        init();
    }
}

Spring Context Utility Class

Implement ApplicationContextAware to manage bean loading and instantiation.

@Component
public class ApplicationContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextUtil.applicationContext = applicationContext;
    }

    /** Get context */
    public static ApplicationContext getContext() {
        return applicationContext;
    }
}

Cache Preheat Handler

Implement CommandLineRunner so the run method executes automatically after startup. Use ApplicationContextUtil to get the context, then getBeansOfType to retrieve all AbstractCache implementations. Instantiate each via getBean (polymorphic) and call init(). If multiple implementations exist, use @Order to control execution order.

@Component
@ConditionalOnProperty(name = {"cache.init.enable"}, havingValue = "true", matchIfMissing = false)
public class CachePreheatHandler implements CommandLineRunner {
    /** Cache preheat */
    @Override
    public void run(String... args) throws Exception {
        ApplicationContext context = ApplicationContextUtil.getContext();
        Map<String, AbstractCache> beansOfType = context.getBeansOfType(AbstractCache.class);
        for (Map.Entry<String, AbstractCache> cacheEntry : beansOfType.entrySet()) {
            AbstractCache cache = context.getBean(cacheEntry.getValue().getClass());
            cache.init();
        }
    }
}

Explanation: @ConditionalOnProperty requires cache.init.enable=true in the configuration file (default false).

cache.init.enable=true

Usage Example: News Hot Data

Assume a tb_news table containing Weibo hot-search sports data.

Database table tb_news
Database table tb_news

Create an AbstractCache implementation for the news module.

@Component
@RequiredArgsConstructor
public class NewsCache extends AbstractCache {
    private static final String NEWS_KEY = "news";
    private final RedisTemplate<String, Object> 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> 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);
    }
}

After starting the project, the hot data is already stored in Redis.

Redis data after preheat
Redis data after preheat

Finally, retrieve data via the get method without worrying about expiration.

@RestController
@RequestMapping("/news")
@RequiredArgsConstructor
public class NewsController {
    private final NewsCache newsCache;

    @GetMapping("/cache")
    public List<News> list() {
        return newsCache.get();
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javabackend developmentRedisSpring BootCommandLineRunnerCache WarmingCache PreheatingRedisTemplate
IoT Full-Stack Technology
Written by

IoT Full-Stack Technology

Dedicated to sharing IoT cloud services, embedded systems, and mobile client technology, with no spam ads.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.