Comprehensive Guide to Using Caffeine Cache in Java and Spring Boot
This article provides an in‑depth overview of the Caffeine local cache library for Java, covering its concepts, configuration options, loading strategies, eviction policies, asynchronous usage, statistics collection, and step‑by‑step integration with Spring Boot including annotations and practical code examples.
Caffeine is a high‑performance, Java 8‑based local cache library that improves throughput by automatically evicting rarely used entries, offering O(1) access and concurrent safety.
1. Cache Basics
Cache is a space‑for‑time trade‑off mechanism that stores data to accelerate subsequent reads. In Java, common cache solutions include EhCache, Memcached, and local caches like Caffeine.
1.1 Manual Cache Creation
Use put() to add entries and getIfPresent() to read them. To atomically load missing values, use get(key, k -> value). Removal is done with invalidate(). In multithreaded scenarios, get(key, k -> value) blocks competing threads while getIfPresent() returns null immediately.
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.5</version>
</dependency>1.2 LoadingCache (Automatic Loading)
LoadingCache automatically invokes a CacheLoader.load() when a key is absent or expired. It also supports bulk loading via getAll() and loadAll(). Example:
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());1.3 AsyncCache (Asynchronous Access)
AsyncCache returns CompletableFuture for all operations, allowing non‑blocking usage. The default executor is ForkJoinPool.commonPool(), but a custom executor can be set via Caffeine.executor(Executor). Example:
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);2. Eviction Policies
Caffeine supports size‑based (LRU, LFU, FIFO) and time‑based eviction. Policies can be combined; once any condition matches, the entry is evicted.
LRU – Least Recently Used
LFU – Least Frequently Used
FIFO – First In First Out
Four eviction settings are available: size (LFU), weight, time, and reference (rarely used).
3. Statistics
Enable .recordStats() to collect hit/miss counts, load successes/failures, total load time, and eviction count. Example:
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());4. Spring Boot Integration
4.1 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>
</dependency>4.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";
}4.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;
}
}4.4 Annotation Usage
Typical Spring cache annotations: @Cacheable – caches method result. @CachePut – updates cache after method execution. @CacheEvict – removes entries. @Caching – combines multiple annotations.
Example method:
@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;
}Note: Cache works via proxies; internal calls within the same class will bypass caching.
5. Summary
Caffeine offers flexible configuration for size, weight, time, and refresh policies, which can be combined to meet various performance and consistency requirements. When integrated with Spring Boot, developers can leverage familiar annotations to manage caching declaratively, while still having access to low‑level APIs for fine‑grained control.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.
