0Pricing
Spring Boot 4 Complete Guide · Aula

Cache em memória com ajuste do Caffeine

Configure políticas de remoção, expiração e tamanho do Caffeine para caches locais de alto desempenho.

Cache em memória com ajuste do Caffeine é uma aula grátis de Spring Boot 4 Complete Guide no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Spring Boot 4 Complete Guide, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Spring Boot 4 Complete Guide inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

Why Caffeine for Local Caches

Caffeine is a high-performance, near-optimal Java caching library and the default in-memory cache for Spring Boot when it is on the classpath.

  • It uses the Window TinyLFU eviction policy, which beats plain LRU on real-world hit rates.
  • It supports size-based, time-based, and reference-based eviction.
  • It is fully concurrent and lock-free on the read path, ideal for high-throughput services.

In this lesson you will tune Caffeine's eviction, expiry, and size policies so a local cache stays fast without exhausting heap.

Adding Caffeine to a Spring Boot 4 App

Spring Boot auto-configures Caffeine when both spring-boot-starter-cache and the caffeine dependency are present.

Then enable caching with @EnableCaching on a configuration class. Methods annotated with @Cacheable will use the Caffeine-backed CacheManager.

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
    // CaffeineCacheManager is auto-configured
    // when com.github.ben-manes.caffeine:caffeine is on the classpath
}

Maximum Size Eviction

The most common policy for a local cache is bounded size. Use maximumSize to cap the number of entries; Caffeine evicts the least valuable entries (Window TinyLFU) once the bound is exceeded.

  • Pick a size that fits comfortably in heap given your value object footprint.
  • Eviction is not immediate at the boundary; it happens promptly but asynchronously.
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;

@Bean
public CaffeineCacheManager cacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager("products");
    manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10_000)
            .recordStats());
    return manager;
}

Weight-Based Eviction

When entries vary wildly in size, bound the cache by weight instead of count. Provide a maximumWeight and a Weigher that returns each entry's cost.

  • You cannot combine maximumSize and maximumWeight on the same cache.
  • Weights are computed once at insertion and are not updated afterward.
import com.github.benmanes.caffeine.cache.Caffeine;

Caffeine.newBuilder()
        .maximumWeight(50_000_000) // ~50MB budget
        .weigher((String key, byte[] value) -> value.length)
        .build();

expireAfterWrite vs expireAfterAccess

Time-based expiry comes in two flavors:

  • expireAfterWrite: entry expires a fixed duration after it was created or last replaced. Best for data with a known freshness window (e.g. a price valid for 5 minutes).
  • expireAfterAccess: entry expires a duration after its last read or write. Best for keeping hot data alive and dropping idle entries.

You may combine both; the entry expires when either condition fires first.

import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;

Caffeine.newBuilder()
        .maximumSize(10_000)
        .expireAfterWrite(Duration.ofMinutes(5))
        .expireAfterAccess(Duration.ofMinutes(2))
        .build();

Configuring via application.properties

For a single shared spec, Spring Boot lets you skip Java config and use the spring.cache.caffeine.spec property. It accepts the comma-separated Caffeine spec string.

This is convenient but applies the same spec to every cache name; per-cache tuning still requires a programmatic CaffeineCacheManager or a custom CacheLoader.

spring.cache.type=caffeine
spring.cache.cache-names=products,prices
spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=5m,recordStats

refreshAfterWrite for Stale-While-Revalidate

refreshAfterWrite differs from expiry: instead of removing the entry, it asynchronously reloads it after the duration while still serving the old value. This avoids a latency spike on the first request after staleness.

  • It requires a LoadingCache (a cache built with a CacheLoader).
  • Only one thread triggers the refresh; others keep reading the existing value.
  • Combine with a longer expireAfterWrite as a hard ceiling.
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.time.Duration;

LoadingCache<String, String> cache = Caffeine.newBuilder()
        .refreshAfterWrite(Duration.ofMinutes(1))
        .expireAfterWrite(Duration.ofMinutes(10))
        .build(key -> loadFromDatabase(key));

A Standalone Caffeine Demo

Here is a complete, framework-free program demonstrating maximumSize eviction. Insert more entries than the bound and observe that the cache never exceeds its size after cleanup.

This is the kind of micro-benchmark you can run to validate a tuning choice before wiring it into Spring.

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

public class CaffeineDemo {
    public static void main(String[] args) {
        Cache<Integer, String> cache = Caffeine.newBuilder()
                .maximumSize(3)
                .build();

        for (int i = 0; i < 10; i++) {
            cache.put(i, "value-" + i);
        }
        cache.cleanUp(); // force pending eviction work

        System.out.println("Estimated size: " + cache.estimatedSize());
        System.out.println("Get key 9: " + cache.getIfPresent(9));
    }
}

Per-Cache Tuning with Custom Specs

Real services need different policies per cache: a tiny hot lookup table vs a large warm dataset. Subclass or configure CaffeineCacheManager so each name gets its own builder.

One clean approach is registering individual native caches by name on the manager.

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import java.time.Duration;

@Bean
public CaffeineCacheManager cacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager();
    manager.registerCustomCache("prices", Caffeine.newBuilder()
            .maximumSize(1_000)
            .expireAfterWrite(Duration.ofSeconds(30))
            .build());
    manager.registerCustomCache("catalog", Caffeine.newBuilder()
            .maximumSize(100_000)
            .expireAfterAccess(Duration.ofHours(1))
            .build());
    return manager;
}

Measuring Hit Rate with recordStats

You cannot tune what you do not measure. Enable recordStats() to expose CacheStats: hit count, miss count, eviction count, and average load penalty.

  • A low hit rate suggests the cache is too small or the keys too cardinal.
  • High eviction with high miss rate means maximumSize is starving the working set.
  • Spring Boot's Micrometer integration publishes these as metrics when stats are on.
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;

public class StatsDemo {
    public static void main(String[] args) {
        Cache<String, Integer> cache = Caffeine.newBuilder()
                .maximumSize(2)
                .recordStats()
                .build();

        cache.put("a", 1);
        cache.getIfPresent("a"); // hit
        cache.getIfPresent("b"); // miss

        CacheStats stats = cache.stats();
        System.out.printf("hits=%d misses=%d rate=%.2f%n",
                stats.hitCount(), stats.missCount(), stats.hitRate());
    }
}

Avoiding Common Tuning Pitfalls

A few traps to watch for under high throughput:

  • Unbounded caches: never build a cache without a size or expiry bound, or you risk an OutOfMemoryError.
  • initialCapacity: set it near the expected steady-state size to avoid resize churn on the read-heavy path.
  • Soft/weak references: softValues() ties eviction to GC pressure, which is unpredictable; prefer explicit size/time bounds for latency-sensitive caches.
  • refreshAfterWrite without a loader: it silently has no effect on a manual cache.

Quick Check: Choosing an Expiry Policy

You cache product prices that the upstream system guarantees are valid for exactly 5 minutes after publication. Reads are frequent but you must never serve a price older than 5 minutes. Which Caffeine policy fits best?

Recap

You tuned Caffeine for high-throughput local caching in Spring Boot 4:

  • Size: maximumSize for uniform entries, maximumWeight + Weigher for variable cost.
  • Time: expireAfterWrite for freshness contracts, expireAfterAccess to keep hot data, combine for both ceilings.
  • Refresh: refreshAfterWrite on a LoadingCache for stale-while-revalidate without latency spikes.
  • Config: spring.cache.caffeine.spec for one shared spec, or registerCustomCache for per-cache tuning.
  • Measure: always enable recordStats() and watch hit rate and eviction count to guide further tuning.

Perguntas Frequentes

A aula “Cache em memória com ajuste do Caffeine” é grátis?

Sim — o texto completo de “Cache em memória com ajuste do Caffeine” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Spring Boot 4 Complete Guide, atualize para CoddyKit PRO. O curso de Spring Boot 4 Complete Guide inclui 4 aulas no total.

O que vou aprender em “Cache em memória com ajuste do Caffeine”?

Configure políticas de remoção, expiração e tamanho do Caffeine para caches locais de alto desempenho. Você pratica Spring Boot 4 Complete Guide com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Spring Boot 4 Complete Guide?

Nenhuma experiência prévia é necessária. Spring Boot 4 Complete Guide no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Cache em memória com ajuste do Caffeine”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Spring Boot 4 Complete Guide?

Sim. Cada aula de Spring Boot 4 Complete Guide inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos da abstração de cache do Spring
  2. Cache em memória com ajuste do Caffeine
  3. Cache distribuído com Redis e TTLs
  4. Esgotamento de cache, invalidação e consistência
← Voltar para Spring Boot 4 Complete Guide