Comprehensive Guide to Using Caffeine Cache in Java and Spring Boot

This article provides an in‑depth tutorial on the Caffeine local cache library for Java, covering its fundamentals, loading strategies, eviction policies, refresh mechanisms, statistics, and step‑by‑step integration with Spring Boot, including configuration, annotations, and practical code examples.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Comprehensive Guide to Using Caffeine Cache in Java and Spring Boot

1. Introduction to Caffeine

Caffeine is a high‑performance local caching library for Java 8, derived from Guava and now the default cache implementation in Spring 5. It offers O(1) access time, concurrent support, and automatic eviction of rarely used entries based on configurable policies.

1.1 Cache Basics

Caching is a space‑for‑time trade‑off that speeds up data access. In Java, common cache solutions include EhCache, Memcached, and Redis for distributed caching. Caffeine serves as an excellent local cache alternative.

1.2 Adding Caffeine Dependency

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.0.5</version>
</dependency>

2. Cache Loading Strategies

2.1 Manual Cache

Use put() to insert entries and getIfPresent() to retrieve them. get(key, k->value) atomically loads a missing value, while invalidate() removes an entry.

Cache<Object, Object> cache = Caffeine.newBuilder()
    .initialCapacity(10)
    .maximumSize(10)
    .expireAfterWrite(1, TimeUnit.SECONDS)
    .expireAfterAccess(1, TimeUnit.SECONDS)
    .removalListener((k, v, cause) -> { })
    .recordStats()
    .build();

cache.put("1", "张三");
System.out.println(cache.getIfPresent("1"));
System.out.println(cache.get("2", k -> "默认值"));

2.2 LoadingCache (Automatic Loading)

Define a CacheLoader so that get() automatically loads missing values. getAll() iterates over keys and invokes loadAll() if implemented.

LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
    .refreshAfterWrite(10, TimeUnit.SECONDS)
    .expireAfterWrite(10, TimeUnit.SECONDS)
    .expireAfterAccess(10, TimeUnit.SECONDS)
    .maximumSize(10)
    .build(key -> new Date().toString());

2.3 AsyncCache (Asynchronous Retrieval)

AsyncCache returns CompletableFuture for non‑blocking access. The default executor is ForkJoinPool.commonPool(), but a custom executor can be supplied via Caffeine.executor(Executor).

AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.SECONDS)
    .expireAfterWrite(1, TimeUnit.SECONDS)
    .expireAfterAccess(1, TimeUnit.SECONDS)
    .maximumSize(10)
    .buildAsync(key -> {
        Thread.sleep(1000);
        return new Date().toString();
    });

CompletableFuture<String> future = asyncLoadingCache.get("1");
future.thenAccept(System.out::println);

3. Eviction Policies

Caffeine supports size‑based, weight‑based, time‑based, and reference‑based eviction. Common strategies include LRU (least recently used), LFU (least frequently used), and FIFO (first in, first out). Example configuration:

Caffeine.newBuilder()
    .maximumSize(10)               // size‑based (LFU)
    .maximumWeight(100)            // weight‑based (mutually exclusive with size)
    .expireAfterWrite(1, TimeUnit.SECONDS)   // time‑based
    .expireAfterAccess(1, TimeUnit.SECONDS)  // time‑based
    .removalListener((k, v, cause) -> log.info("Evicted {}", k));

4. Refresh Mechanism

refreshAfterWrite()

triggers a refresh after a specified interval, but only for LoadingCache or AsyncLoadingCache. The refresh occurs on the next read after the interval.

private static int NUM = 0;

LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.SECONDS)
    .build(k -> ++NUM);

System.out.println(cache.get(1)); // 1
Thread.sleep(2000);
System.out.println(cache.getIfPresent(1)); // still 1
System.out.println(cache.getIfPresent(1)); // now 2 after refresh

5. Statistics

Enable recordStats() to collect hit/miss counts, load times, eviction counts, etc.

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.SECONDS)
    .expireAfterWrite(1, TimeUnit.SECONDS)
    .expireAfterAccess(1, TimeUnit.SECONDS)
    .maximumSize(10)
    .recordStats()
    .build(key -> { Thread.sleep(1000); return new Date().toString(); });

cache.put("1", "shawn");
cache.get("1");
System.out.println(cache.stats());

6. Spring Boot Integration

6.1 Required Annotations and Dependencies

Add @EnableCaching to a configuration class and include spring-boot-starter-cache and the Caffeine dependency.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

6.2 Cache Constants

public class CacheConstants {
    public static final int DEFAULT_EXPIRES = 3 * 60;
    public static final int EXPIRES_5_MIN = 5 * 60;
    public static final int EXPIRES_10_MIN = 10 * 60;
    public static final String GET_USER = "GET:USER";
    public static final String GET_DYNAMIC = "GET:DYNAMIC";
}

6.3 Cache Configuration

@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager manager = new SimpleCacheManager();
        List<CaffeineCache> caches = new ArrayList<>();
        for (CacheEnum e : CacheEnum.values()) {
            caches.add(new CaffeineCache(e.getName(),
                Caffeine.newBuilder()
                    .initialCapacity(50)
                    .maximumSize(1000)
                    .expireAfterAccess(e.getExpires(), TimeUnit.SECONDS)
                    .build()));
        }
        manager.setCaches(caches);
        return manager;
    }
}

6.4 Using Cache Annotations

Typical annotations include @Cacheable, @CachePut, @CacheEvict, and @Caching. Example:

@Cacheable(value = CacheConstants.GET_USER, key = "'user'+#userId", sync = true)
@CacheEvict
public UserEntity getUserByUserId(Integer userId) {
    UserEntity user = userMapper.findById(userId);
    System.out.println("Queried DB");
    return user;
}

7. Summary

Caffeine provides a flexible, high‑performance local cache for Java applications. By combining size, weight, and time‑based eviction with optional refresh and statistics, developers can fine‑tune caching behavior. Spring Boot integration is straightforward through standard caching annotations and a simple CacheManager bean.

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.

JavacachingCaffeineSpringBoot
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.