Mastering Caffeine Cache in Spring Boot: Algorithms, Configurations, and Best Practices

This article explains how Caffeine Cache improves on Guava Cache with the modern W‑TinyLFU algorithm, details its eviction strategies, shows how to configure it in Spring Boot, and provides code examples for manual, synchronous, and asynchronous loading, as well as annotation‑driven usage.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Mastering Caffeine Cache in Spring Boot: Algorithms, Configurations, and Best Practices

Caffeine Cache Overview

While Guava Cache offers basic get/put operations, thread‑safety, expiration, and eviction, Caffeine Cache builds on Guava’s ideas and introduces a more efficient eviction algorithm called W‑TinyLFU.

Algorithmic Advantages

Caffeine replaces simple LRU with a hybrid approach that combines FIFO, LRU, and LFU, addressing each algorithm’s drawbacks. LFU provides high hit rates when access patterns are stable, but struggles with changing workloads. LRU handles bursts well but may evict hot items prematurely. W‑TinyLFU merges LFU’s long‑term frequency tracking with LRU’s short‑term recency handling.

W‑TinyLFU uses a Count‑Min Sketch to record recent access frequencies in a compact data structure, applying a sliding‑window decay to adapt to changing patterns. This design efficiently filters which entries are eligible for insertion, achieving near‑optimal hit rates.

When access patterns are stable, LFU yields the best hit rate, but it incurs high overhead for maintaining frequency counters and cannot adapt quickly to shifts in popularity.

Cache Filling Strategies

Manual Loading

public Object manualOperator(String key) {
    Cache<String, Object> cache = Caffeine.newBuilder()
        .expireAfterWrite(1, TimeUnit.SECONDS)
        .expireAfterAccess(1, TimeUnit.SECONDS)
        .maximumSize(10)
        .build();
    Object value = cache.get(key, k -> setValue(k).apply(k));
    cache.put("hello", value);
    Object present = cache.getIfPresent(key);
    cache.invalidate(key);
    return value;
}

public Function<String, Object> setValue(String key) {
    return k -> key + "value";
}

Synchronous Loading

public Object syncOperator(String key) {
    LoadingCache<String, Object> cache = Caffeine.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .build(k -> setValue(k).apply(k));
    return cache.get(key);
}

public Function<String, Object> setValue(String key) {
    return k -> key + "value";
}

Asynchronous Loading

public Object asyncOperator(String key) {
    AsyncLoadingCache<String, Object> cache = Caffeine.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .buildAsync(k -> setAsyncValue(k).get());
    return cache.get(key);
}

public CompletableFuture<Object> setAsyncValue(String key) {
    return CompletableFuture.supplyAsync(() -> key + "value");
}

Eviction Policies

Caffeine supports three main eviction strategies:

Size‑based eviction (maximumSize or maximumWeight)

Time‑based eviction (expireAfterAccess, expireAfterWrite, custom Expiry)

Reference‑based eviction (weakKeys, weakValues, softValues)

Size‑based eviction can be configured by entry count or weight; time‑based eviction removes entries after a fixed period of inactivity or write; reference‑based eviction lets the garbage collector reclaim entries when only weak/soft references remain.

Integration with Spring Boot

Spring Boot 2.x replaces Guava Cache with Caffeine as the default local cache. To use Caffeine:

Add dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.6.2</version>
</dependency>

Enable caching with @EnableCaching on the main application class.

Configure cache specifications via application.properties or application.yml, e.g.:

spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=10s

Or define beans for fine‑grained control, creating CaffeineCache instances with custom TTL, maximum size, and statistics recording.

Annotation‑Driven Cache Operations

Spring’s cache annotations work with Caffeine:

@Cacheable(value = "userCache", key = "#id", sync = true)
public User getUser(Long id) { /* query DB */ }

@CachePut(value = "userCache", key = "#user.id")
public User saveUser(User user) { /* persist */ }

@CacheEvict(value = "userCache", key = "#user.id")
public void deleteUser(User user) { /* remove */ }

These annotations automatically handle cache reads, writes, and evictions based on method execution.

Cache Writer and Removal Listener

Cache<String, Object> cache = Caffeine.newBuilder()
    .removalListener((k, v, cause) ->
        System.out.printf("Key %s was removed (%s)%n", k, cause))
    .writer(new CacheWriter<String, Object>() {
        public void write(String key, Object value) { /* write to external store */ }
        public void delete(String key, Object value, RemovalCause cause) { /* delete from store */ }
    })
    .build();

Statistics

Enable statistics with .recordStats() and retrieve metrics such as hit rate, eviction count, and average load penalty via cache.stats().

Reference Types in Java

Caffeine supports weak and soft references for keys and values, allowing the garbage collector to reclaim entries when memory is low. Weak keys/values use identity comparison; soft values are reclaimed only under memory pressure.

Count‑Min Sketch illustration
Count‑Min Sketch illustration
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.

cachingCaffeinecache-configurationspring-boot
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.