Implementing Two-Level Cache with Caffeine and Redis in Spring Boot
This article explains the design and implementation of a two‑level caching architecture using local Caffeine cache as L1 and remote Redis as L2 in Spring Boot, covering manual cache handling, annotation‑based management with Spring Cache, and a custom annotation with AOP to minimize code intrusion.
In high‑performance service architecture, caching is essential; remote caches like Redis or Memcached reduce database load, but combining them with a local cache (Caffeine or Guava) forms a two‑level cache that further improves response time.
Advantages: local cache provides ultra‑fast memory access and reduces network I/O; however, consistency between L1, L2 and the database must be maintained, especially in distributed environments where cache invalidation is required.
Preparation
Add the following Maven dependencies to a Spring Boot project:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.1</version>
</dependency>Configure Redis connection in application.yml :
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
timeout: 10000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0V1 – Manual Two‑Level Cache
Define a Caffeine bean:
@Configuration
public class CaffeineConfig {
@Bean
public Cache
caffeineCache() {
return Caffeine.newBuilder()
.initialCapacity(128)
.maximumSize(1024)
.expireAfterWrite(60, TimeUnit.SECONDS)
.build();
}
}Use the cache directly in service methods, e.g. query:
public Order getOrderById(Long id) {
String key = CacheConstant.ORDER + id;
Order order = (Order) cache.get(key, k -> {
Object obj = redisTemplate.opsForValue().get(k);
if (obj != null) {
log.info("get data from redis");
return obj;
}
log.info("get data from database");
Order dbOrder = orderMapper.selectOne(new LambdaQueryWrapper
().eq(Order::getId, id));
redisTemplate.opsForValue().set(k, dbOrder, 120, TimeUnit.SECONDS);
return dbOrder;
});
return order;
}Update and delete operations manually synchronize both Caffeine and Redis caches.
V2 – Spring Cache Annotations
Configure a CaffeineCacheManager and enable caching with @EnableCaching . Then annotate service methods:
@Cacheable(value = "order", key = "#id")
public Order getOrderById(Long id) { … } @CachePut(cacheNames = "order", key = "#order.id")
public Order updateOrder(Order order) { … } @CacheEvict(cacheNames = "order", key = "#id")
public void deleteOrder(Long id) { … }Spring handles the local cache automatically, while Redis updates remain explicit.
V3 – Custom Annotation with AOP
Define a @DoubleCache annotation that specifies cache name, key (Spring EL), L2 timeout and operation type (FULL, PUT, DELETE). Implement an aspect that parses the EL key, builds the real cache key, and performs read‑through, write‑through or eviction logic against both Caffeine and Redis.
Service methods become concise:
@DoubleCache(cacheName = "order", key = "#id", type = CacheType.FULL)
public Order getOrderById(Long id) { … } @DoubleCache(cacheName = "order", key = "#order.id", type = CacheType.PUT)
public Order updateOrder(Order order) { … } @DoubleCache(cacheName = "order", key = "#id", type = CacheType.DELETE)
public void deleteOrder(Long id) { … }Conclusion
The article demonstrates three approaches—manual two‑level cache, Spring’s annotation‑driven cache, and a custom AOP‑based annotation—to reduce cache‑related code intrusion in Spring Boot applications, while highlighting consistency, expiration, and concurrency considerations.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.