Spring Boot
Caffeine Cache
Cache Specification
Java
Programming Tips

Is it possible to set a different specification per cache using caffeine in spring boot?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Yes, you can give different Caffeine caches different policies in a Spring Boot application, but not through the single global spring.cache.caffeine.spec property alone. That property applies one specification to all caches managed by the default CaffeineCacheManager. If you need per-cache expiration, size, or refresh settings, create the caches explicitly in configuration.

The Global Property Is Shared

Spring Boot makes the simple case easy:

properties
spring.cache.type=caffeine
spring.cache.cache-names=users,products
spring.cache.caffeine.spec=maximumSize=1000,expireAfterWrite=10m

This is convenient when every cache should behave the same way, but it does not let users expire after 5 minutes while products expire after 1 hour.

Create Each Cache with Its Own Builder

When policies differ per cache, define the caches yourself and register them with a SimpleCacheManager.

java
1package com.example.demo;
2
3import com.github.benmanes.caffeine.cache.Caffeine;
4import java.util.List;
5import java.util.concurrent.TimeUnit;
6import org.springframework.cache.CacheManager;
7import org.springframework.cache.caffeine.CaffeineCache;
8import org.springframework.cache.support.SimpleCacheManager;
9import org.springframework.context.annotation.Bean;
10import org.springframework.context.annotation.Configuration;
11
12@Configuration
13public class CacheConfig {
14
15    @Bean
16    public CacheManager cacheManager() {
17        CaffeineCache usersCache = new CaffeineCache(
18            "users",
19            Caffeine.newBuilder()
20                .maximumSize(1_000)
21                .expireAfterWrite(5, TimeUnit.MINUTES)
22                .build()
23        );
24
25        CaffeineCache productsCache = new CaffeineCache(
26            "products",
27            Caffeine.newBuilder()
28                .maximumSize(10_000)
29                .expireAfterAccess(1, TimeUnit.HOURS)
30                .build()
31        );
32
33        SimpleCacheManager manager = new SimpleCacheManager();
34        manager.setCaches(List.of(usersCache, productsCache));
35        return manager;
36    }
37}

This approach gives each cache its own Caffeine builder, which is exactly what you need for per-cache specifications.

Use the Caches Normally with @Cacheable

Once the cache manager is configured, the service code looks normal.

java
1package com.example.demo;
2
3import org.springframework.cache.annotation.Cacheable;
4import org.springframework.stereotype.Service;
5
6@Service
7public class CatalogService {
8
9    @Cacheable("users")
10    public String loadUser(long id) {
11        return "user-" + id;
12    }
13
14    @Cacheable("products")
15    public String loadProduct(long id) {
16        return "product-" + id;
17    }
18}

The cache name selects the already-configured cache instance, so the service does not need to know which expiration policy it is using.

This separation is useful operationally as well. If you later decide that users should refresh faster or products should hold more entries, you can change cache policy in one configuration class without touching every cached service method.

It also makes startup behavior explicit. You know exactly which caches exist and what their policies are instead of discovering them lazily at runtime under a one-size-fits-all builder.

Why CaffeineCacheManager Alone Is Not Enough

CaffeineCacheManager is convenient when one builder or one spec should apply to every cache it creates lazily. That is its strength, but it is also the reason it cannot express radically different cache policies by itself through one property string.

If all caches are similar, keep the simpler manager. If each cache has different operational requirements, explicit registration is clearer and more predictable.

Common Pitfalls

The biggest mistake is expecting spring.cache.caffeine.spec to support named per-cache fragments. In the default Boot setup, it does not. It is a single spec shared across the manager.

Another issue is mixing explicit cache beans with lazy cache creation without realizing which manager is active. If the application still auto-configures a different cache manager than the one you intended, your policies may not apply where you expect.

Developers also sometimes optimize only expiration and forget capacity. Different caches often need different maximum sizes as well as different time-based rules.

Finally, do not create custom cache policies without looking at cache usage patterns. A short TTL and tiny maximum size may be perfect for fast-changing user state and terrible for mostly static catalog data.

Summary

  • A single Boot Caffeine spec property applies globally, not per cache.
  • Use explicit cache configuration when different caches need different policies.
  • 'SimpleCacheManager with separately built CaffeineCache instances is a clear solution.'
  • Service code can keep using @Cacheable normally once the caches are registered.
  • Pick expiration and size settings based on each cache's access pattern, not just convenience.

Course illustration
Course illustration