Caffeine Cache Introduction, Basics, and Spring Boot Integration
This article introduces the Java Caffeine caching library, explains its manual, loading, and async cache APIs, details eviction and refresh strategies, shows how to collect statistics, and demonstrates full integration with Spring Boot using Maven dependencies, cache annotations, constant definitions, configuration classes, and usage examples.
Caffeine is a high‑performance local caching library for Java 8, derived from Guava and used as the default cache implementation in Spring 5. It provides O(1) access, automatic eviction of rarely used entries, and can be combined with various eviction and expiration policies.
1. Cache Basics
Cache is a space‑time trade‑off technique that stores data to accelerate subsequent accesses. In Java, common cache solutions include EhCache, Memcached, and local caches like Caffeine.
2. Using Caffeine
Include the Maven dependency:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.5</version>
</dependency>2.1 Manual Cache
Operations such as put() , get(key, k->value) , and invalidate() are provided. get(key, k->value) atomically loads a missing value, avoiding write contention.
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 entries. The cache can be refreshed after a write interval.
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
AsyncCache returns CompletableFuture for non‑blocking retrieval. 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);2.4 Eviction Policies
Caffeine supports size‑based (LRU, LFU, FIFO) and time‑based eviction. Four eviction settings are available: size (LFU), weight, time, and reference.
2.5 Statistics
Enable recordStats() to obtain hit/miss counts, load success/failure, total load time, and eviction count.
LoadingCache<String,String> cache = Caffeine.newBuilder()
.recordStats()
.build(key -> {
Thread.sleep(1000);
return new Date().toString();
});
cache.put("1","shawn");
cache.get("1");
System.out.println(cache.stats());3. Summary of Strategies
Two typical configurations are recommended: (1) set maxSize and refreshAfterWrite without expireAfterWrite/expireAfterAccess , using expireAfterWrite for better performance when stale data is acceptable; (2) set maxSize together with expireAfterWrite/expireAfterAccess for stronger consistency at the cost of performance.
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 Annotations
Common annotations include @Cacheable , @CachePut , @CacheEvict , and @Caching . Attributes such as cacheNames/value , key , keyGenerator , cacheManager , condition , unless , and sync control caching behavior.
4.3 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.4 Cache Configuration
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
List
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()));
}
cacheManager.setCaches(caches);
return cacheManager;
}
}4.5 Using the Cache
Example of a cached method with synchronization:
@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 database");
return user;
}The article concludes with a reminder that cache proxies behave like transactional proxies; internal calls bypass the proxy and therefore do not trigger caching.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.