FallstudienBlogÜber uns
Anfragen

Java-Caching mit Spring

Alexander Stasiak

10. Feb. 202612 Min. Lesezeit

JavaSpring BootCaching Strategies

Inhaltsverzeichnis

  • Caching in Java‑Anwendungen verstehen

  • Erste Schritte: Caching in einem Spring Boot‑Projekt aktivieren

    • Spring Boot Starter Cache verwenden

    • Spring Core Caching ohne Spring Boot verwenden

  • Spring‑Caching aktivieren und konfigurieren

    • Cache‑Namen und ‑Regionen definieren

  • Caching mit Spring‑Annotationen verwenden

    • @Cacheable: Methodenergebnisse cachen

    • @CacheEvict: Veraltete Einträge entfernen

    • @CachePut: Cache‑Updates erzwingen

    • @Caching: Mehrere Cache‑Operationen kombinieren

    • @CacheConfig: Cache‑Einstellungen zentralisieren

  • Bedingtes Caching und fortgeschrittene Key‑Strategien

    • Das Attribut condition verwenden

    • Das Attribut unless verwenden

  • Java‑basierte Cache‑Konfigurationen integrieren

    • In‑Memory‑Cache konfigurieren (z. B. Caffeine)

    • Verteilten Cache konfigurieren (z. B. Redis)

  • Best Practices, Fallstricke und Fazit

Wenn Ihre Spring Boot-Anwendung wiederholt dieselben Daten aus einer Datenbank oder einem externen API abruft, verschwenden Sie vermutlich Millisekunden – oder sogar Sekunden – bei jeder Anfrage. Java-Caching mit Spring löst das, indem häufig benötigte Ergebnisse im schnellen Speicher abgelegt werden, sodass nachfolgende Aufrufe den teuren Vorgang vollständig überspringen.

Die Cache-Abstraktion von Spring, eingeführt in Spring Framework 3.1 um 2011 und mit Spring Boot 1.0 in 2014 deutlich verfeinert, bietet einen deklarativen, annotationsgetriebenen Ansatz für Caching. Der große Vorteil: Sie entkoppelt Ihre Geschäftslogik vom zugrunde liegenden Cache-Provider. Ob Sie für die Entwicklung einen einfachen In‑Memory‑Cache nutzen oder in der Produktion Redis einsetzen – Ihr Service-Code bleibt unverändert.

Diese Anleitung zeigt, wie Sie Caching in einem modernen Spring Boot 3 / Java 17-Projekt aktivieren, konfigurieren und nutzen. Das nehmen Sie mit:

  • Bessere Performance: Reduzieren Sie Antwortzeiten von hunderten Millisekunden auf unter 1 ms für gecachte Operationen
  • Einfacheres Cache-Management: Caching per Annotation statt Boilerplate-Logik
  • Leichtere Provider-Migration: Wechseln Sie zwischen ConcurrentMap, Caffeine, Redis oder Ehcache ohne Änderungen an der Geschäftslogik
  • Produktionsreife Muster: Lernen Sie Conditional Caching, Cache Eviction und Multi-Cache-Strategien

Caching in Java‑Anwendungen verstehen

Caching speichert häufig verwendete Daten in schnellem Speicher – typischerweise RAM –, um wiederholte, teure Operationen zu vermeiden. In Java-Backends bedeutet das, Methodenaufrufe abzufangen und bereits berechnete Ergebnisse zurückzugeben, statt dieselbe Logik immer wieder auszuführen.

Konkrete Szenarien, in denen Caching glänzt:

  • Produktdetails via JPA: Ein E‑Commerce‑Service ruft productRepository.findById(productId) für populäre Artikel tausendfach pro Stunde auf
  • User-Profildaten aus einem externen REST API: Jeder Lookup dauert wegen Latenz 150–300 ms
  • Konfigurationswerte aus einem Remote‑Service: Feature Flags und Settings, die selten wechseln, aber bei jedem Seitenaufruf benötigt werden

Die Performance-Auswirkung ist erheblich. Ein Datenbankaufruf benötigt meist 200–300 ms (Connection-Overhead, Query-Ausführung, Mapping). Ein Cache-Lookup ist in unter 1 ms erledigt. Bei einem stark frequentierten Endpoint mit 10.000 Requests pro Stunde summiert sich der Unterschied täglich zu Stunden an gesparter Rechenzeit.

Caching adressiert typische Probleme:

  • Hohe Latenz bei wiederholten Reads derselben Daten
  • Datenbank-Engpässe unter Last, wenn viele Requests identische Queries ausführen
  • Drosselung bei Drittanbieter-APIs mit Rate Limits
  • Überflüssige Compute-Zyklen für deterministische Berechnungen

Die Cache-Abstraktion von Spring zielt speziell auf Methodenergebnisse. Das ist etwas anderes als HTTP‑Caching oder CDN‑Caching, das von einem Content Delivery Network übernommen wird. Diese Ebenen cachen Antworten näher am Client, während Spring‑Caching in der Service-Schicht Ihrer Web‑Anwendung arbeitet.

Erste Schritte: Caching in einem Spring Boot‑Projekt aktivieren

Dieser Abschnitt zeigt, wie Sie aus einer „plain“ Spring Boot 3‑Anwendung – erstellt mit Spring Initializr in 2025 – in wenigen Minuten eine Anwendung mit Basis‑Caching machen.

Stellen Sie sicher, dass Ihr Projekt nutzt:

  • Java 17 oder höher
  • Spring Boot 3.x
  • Maven (pom.xml) oder Gradle (build.gradle.kts) als Build-Tool

Die Abhängigkeit spring-boot-starter-cache muss hinzugefügt werden. Dieser Starter bringt das Modul spring-context-support und alles Notwendige für die Caching-Infrastruktur mit. Für ein einfaches Demo funktioniert der standardmäßige ConcurrentMapCacheManager ohne weitere Provider-Abhängigkeit.

Der Kernschritt ist, @EnableCaching in Ihrer Spring Boot‑Main-Klasse oder einer dedizierten Konfiguration zu ergänzen. So sieht das aus:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class BookstoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookstoreApplication.class, args);
    }
}

Mit diesen beiden Bausteinen – der Abhängigkeit und der Annotation – ist Caching einsatzbereit.

Spring Boot Starter Cache verwenden

Das Hinzufügen von spring-boot-starter-cache aktiviert die Auto-Konfiguration der Caching-Infrastruktur. Das ist für die meisten Projekte der empfohlene Weg.

Fügen Sie diese Abhängigkeit zu Ihrer pom.xml hinzu:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

Für Gradle fügen Sie implementation 'org.springframework.boot:spring-boot-starter-cache' im dependencies-Block hinzu.

Das leistet die Auto-Konfiguration:

  • Registriert einen Default-CacheManager basierend auf dem, was auf dem Classpath verfügbar ist
  • Scannt nach Caching-Annotationen, sobald @EnableCaching vorhanden ist
  • Verdrahtet Caches anhand der in application.yml oder application.properties definierten Properties
  • Stellt sinnvolle Defaults bereit, die sofort funktionieren

Dieser Starter ist der gleiche Einstiegspunkt – egal, ob Ihr finaler Cache-Provider Caffeine, Redis oder eine einfache In‑Memory‑Map ist. Der einzige Unterschied sind die zusätzlichen Abhängigkeiten, die Sie einbinden.

Spring Core Caching ohne Spring Boot verwenden

Caching funktioniert auch in „plain“ Spring‑Anwendungen – z. B. Spring Framework 6.x ohne Boot. In diesem Fall fügen Sie spring-context und das Modul spring-context-support manuell hinzu.

Ohne Boot‑Auto‑Konfiguration müssen Sie einen CacheManager‑Bean explizit in einer Konfiguration definieren:

  • Erstellen Sie eine Java‑Config‑Klasse mit @EnableCaching
  • Fügen Sie eine @Bean‑Methode hinzu, die Ihre gewünschte CacheManager‑Implementierung zurückgibt
  • Beispiel: return new ConcurrentMapCacheManager("books", "users") mit vordefinierten Cache‑Namen
  • Optional konfigurieren Sie einen ausgefeilteren Manager wie CaffeineCacheManager

Dieser Ansatz ist üblich in Legacy‑Java‑EE‑Deployments, eigenständigen Spring‑Bibliotheken oder wenn die Meinungen von Spring Boot nicht zu Ihren Anforderungen passen.

Spring‑Caching aktivieren und konfigurieren

Sobald die Abhängigkeiten stehen, aktivieren Sie das Caching-Verhalten und legen fest, wie Ihr CacheManager arbeiten soll.

Die Annotation @EnableCaching triggert einen Post‑Processor, der Spring‑Beans nach Caching‑Annotationen scannt und Proxys darum legt. Diese Proxys fangen Methodenaufrufe ab, um vor der Ausführung nach gecachten Ergebnissen zu prüfen.

Spring Boot wählt die Standardkonfiguration für den CacheManager per Classpath‑Erkennung:

Provider auf dem ClasspathVerwendeter CacheManager
Keiner (nur Starter)ConcurrentMapCacheManager
CaffeineCaffeineCacheManager
Redis (spring-boot-starter-data-redis)RedisCacheManager
EhCacheEhCacheCacheManager

Für den Produktivbetrieb empfiehlt sich eine explizite Konfiguration. Definieren Sie einen CaffeineCacheManager oder RedisCacheManager‑Bean, um zu steuern:

  • Time‑to‑Live (TTL) für Cache‑Einträge
  • Maximale Größe vor Eviction
  • Eviction‑Strategien (LRU, LFU, größenbasiert)
  • Pro‑Cache‑Konfigurationen mit unterschiedlichen Policies

Spring stellt außerdem CacheManagerCustomizer<T extends CacheManager> als Hook bereit, um von Auto‑Konfiguration erzeugte Caches feinzujustieren, ohne sie komplett zu ersetzen.

Cache‑Namen und ‑Regionen definieren

Caches sind in Spring nach logischen Namen gruppiert – Strings wie „products“, „users“ oder „exchangeRates“. Diese Namen mappen auf Regionen/Räume beim zugrunde liegenden Provider.

Best Practices für die Benennung:

  • Wählen Sie aussagekräftige Namen, die die Daten widerspiegeln (z. B. „productById“ statt „cache1“)
  • Halten Sie Namen in Annotationen und Konfigurationsdateien konsistent
  • Verwenden Sie konsequent Singular oder Plural
  • Erwägen Sie in größeren Systemen Präfixe mit Servicenamen („catalog-products“, „pricing-rates“)

Beispiel für eine ProductService‑Klasse:

  • „productById“-Cache für Einzelabrufe per ID
  • „allProducts“-Cache für Listing‑Endpoints, die Collections zurückgeben
  • „productSearch“-Cache für Suchergebnisse mit Query‑Parametern als Keys

Einige Provider wie Redis und Ehcache erlauben pro Region eigene Konfigurationen in einer XML‑Datei oder application.yml, etwa unterschiedliche TTLs und Größen je Cache‑Name.

Caching mit Spring‑Annotationen verwenden

Die Methoden‑Annotationen sind der Hauptweg, um Daten in Ihren Services zu cachen. Das Framework stellt alle nötigen Caching‑Annotationen bereit:

AnnotationZweck
@CacheableMethodenergebnisse cachen; Ausführung bei Cache‑Treffer überspringen
@CachePutMethode immer ausführen; Cache mit Resultat aktualisieren
@CacheEvictEinträge aus dem Cache entfernen
@CachingMehrere Caching‑Annotationen auf einer Methode kombinieren
@CacheConfigGemeinsame Cache‑Einstellungen auf Klassenebene setzen

Diese Annotationen sitzen auf öffentlichen Methoden von Spring‑Beans – typischerweise Klassen mit @Service oder @Repository. Die beim Aktivieren des Cachings erzeugten Proxys fangen Aufrufe ab und setzen das Caching‑Verhalten um.

Standardmäßig bilden die Methodenparameter den Cache‑Schlüssel. Für eine Methode getBook(String isbn) wird die ISBN zum Key. Sie können das mit der Spring Expression Language (SpEL) überschreiben.

Das folgende Beispiel demonstriert die Annotationen in einem BookService.

@Cacheable: Methodenergebnisse cachen

@Cacheable ist für Lese‑Operationen gedacht, deren Rückgabewert wiederverwendbar ist. Denken Sie an findById, getDetails oder Lookup‑Methoden, die bei gleichen Parametern dasselbe Ergebnis liefern.

@Service
public class BookService {
    
    private final BookRepository bookRepository;
    
    @Cacheable(cacheNames = "books", key = "#isbn")
    public Book getBookByIsbn(String isbn) {
        // This method body only executes on cache miss
        simulateSlowService();
        return bookRepository.findByIsbn(isbn);
    }
    
    private void simulateSlowService() {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

So funktioniert es:

  1. Erster Aufruf mit ISBN „978-0134685991“: Cache‑Miss → Methode läuft → Ergebnis wird im „books“-Cache gespeichert
  2. Zweiter Aufruf mit derselben ISBN: Cache‑Hit → Methodenrumpf wird übersprungen → gecachtes Ergebnis kommt sofort zurück
  3. Aufruf mit anderer ISBN: Cache‑Miss für diesen Key → Methode läuft erneut

Die Annotation prüft vor der Ausführung den Cache. Existiert ein Eintrag für den Key, werden der zugrunde liegende Datenbankzugriff oder die Berechnung vollständig vermieden.

Wann @Cacheable einsetzen:

  • Katalogdaten, die sich selten ändern
  • Benutzereinstellungen und Präferenzen
  • Referenzdaten wie Länder, Währungen oder Kategorien

Wann @Cacheable vermeiden:

  • Schreiblastige Methoden, die Zustand ändern
  • Methoden mit Nebeneffekten über die Rückgabe hinaus
  • Echtzeitdaten, die stets frisch sein müssen

@CacheEvict: Veraltete Einträge entfernen

@CacheEvict invalidiert Cache‑Einträge, wenn sich zugrunde liegende Daten ändern. Nutzen Sie es bei Update‑ oder Delete‑Methoden, um veraltete Daten zu vermeiden.

@CacheEvict(cacheNames = "books", key = "#isbn")
public void updateBookPrice(String isbn, BigDecimal price) {
    bookRepository.updatePrice(isbn, price);
}

Nach der Ausführung entfernt der Cache den Eintrag zur entsprechenden ISBN. Der nächste Read greift auf die Datenbank zu und cached frische Werte.

Für Bulk‑Operationen – etwa nächtliche Syncs oder Importe – den gesamten Cache leeren:

@CacheEvict(cacheNames = "books", allEntries = true)
public void refreshAllBooks() {
    // Bulk import logic
    bookRepository.syncFromExternalCatalog();
}

Die Option beforeInvocation steuert das Timing:

  • beforeInvocation = false (Default): Eviction erfolgt nach erfolgreicher Ausführung
  • beforeInvocation = true: Eviction erfolgt davor, also auch bei Exceptions

Paaren Sie Datenänderungen immer mit passender Cache‑Invalidierung. Vergessene Evictions sind eine der häufigsten Ursachen für veraltete Daten in gecachten Anwendungen.

@CachePut: Cache‑Updates erzwingen

@CachePut führt die Methode immer aus und aktualisiert den Cache mit dem Rückgabewert. Ideal für Schreib‑Operationen, die den Cache ohne ausgelassene Logik auffrischen sollen.

@CachePut(cacheNames = "books", key = "#result.isbn")
public Book saveBook(Book book) {
    return bookRepository.save(book);
}

Beachten Sie den Key #result.isbn – er referenziert den Rückgabewert, also die gespeicherte Entität mit befüllten Feldern.

Zentrale Unterschiede zu @Cacheable:

Aspekt@Cacheable@CachePut
MethodenausführungBei Cache‑Hit übersprungenWird immer ausgeführt
Cache‑AktualisierungNur bei Cache‑MissImmer aktualisiert
Typischer AnwendungsfallLeseoperationenSchreiboperationen

Vermeiden Sie es, @Cacheable und @CachePut auf derselben Methode zu kombinieren. Die widersprüchlichen Verhaltensweisen führen zu Verwirrung. Trennen Sie stattdessen Lese‑ und Schreibmethoden klar.

Nutzen Sie @CachePut, wenn Sie:

  • den Cache nach Saves/Updates warm halten möchten
  • einen nachfolgenden Cache‑Miss nach einem Write vermeiden wollen
  • sicherstellen möchten, dass der Cache nach Mutationen den neuesten Stand widerspiegelt

@Caching: Mehrere Cache‑Operationen kombinieren

Java erlaubt nicht, dieselbe Annotation mehrfach auf einer Methode zu deklarieren. @Caching gruppiert mehrere Caching‑Annotationen.

@Caching(
    evict = {
        @CacheEvict(cacheNames = "books", key = "#book.isbn"),
        @CacheEvict(cacheNames = "bestsellers", allEntries = true)
    },
    put = {
        @CachePut(cacheNames = "books", key = "#result.isbn")
    }
)
public Book updateBookAndRefreshCaches(Book book) {
    return bookRepository.save(book);
}

Diese Methode:

  1. löscht das spezifische Buch aus dem „books“-Cache
  2. leert den gesamten „bestsellers“-Cache (da sich Rankings ändern können)
  3. legt das aktualisierte Buch frisch im „books“-Cache ab

Typische Szenarien für @Caching:

  • Ein einzelnes Update betrifft mehrere Cache‑Regionen
  • Sie brauchen mehrere Annotationen desselben Typs (z. B. Eviction aus drei Caches)
  • Komplexe Workflows, in denen Eviction und Refresh zusammen passieren

Trotz der Flexibilität kann starker Einsatz von @Caching die Lesbarkeit mindern. Setzen Sie es für komplexe, gut dokumentierte Fälle ein und kommentieren Sie, warum mehrere Cache‑Operationen nötig sind.

@CacheConfig: Cache‑Einstellungen zentralisieren

@CacheConfig auf Klassenebene definiert gemeinsame Attribute, sodass Sie sie nicht in jeder Methode wiederholen. Das reduziert Duplikate in Services, die denselben Cache in vielen Methoden nutzen.

@Service
@CacheConfig(cacheNames = "books")
public class BookService {
    
    @Cacheable(key = "#isbn")
    public Book getBookByIsbn(String isbn) {
        return bookRepository.findByIsbn(isbn);
    }
    
    @CacheEvict(key = "#isbn")
    public void deleteBook(String isbn) {
        bookRepository.deleteByIsbn(isbn);
    }
    
    @CachePut(key = "#result.isbn")
    public Book saveBook(Book book) {
        return bookRepository.save(book);
    }
}

Mit @CacheConfig(cacheNames = "books") auf Klassenebene müssen einzelne Methoden nur noch den Key angeben. Der Cache‑Name wird geerbt.

Sie können außerdem konfigurieren:

  • keyGenerator: Den Namen eines benutzerdefinierten Key‑Generators für alle Methoden
  • cacheResolver: Einen Custom Cache Resolver zur dynamischen Cache‑Auswahl
  • cacheManager: Einen spezifischen CacheManager‑Bean, falls Sie mehrere Manager/Provider parallel nutzen

@CacheConfig aktiviert Caching nicht von selbst. Sie benötigen weiterhin @EnableCaching auf Konfigurationsebene, und einzelne Methoden brauchen ihre @Cacheable-, @CachePut- oder @CacheEvict‑Annotationen.

Bedingtes Caching und fortgeschrittene Key‑Strategien

Nicht jeder Methodenaufruf sollte gecacht werden. Manchmal ist Caching nur für bestimmte Inputs sinnvoll oder wenn das Ergebnis bestimmte Kriterien erfüllt. Spring bietet dafür konditionale Attribute.

Alle relevanten Caching‑Annotationen unterstützen zwei SpEL‑basierte Attribute:

AttributAusführungszeitpunktZweck
conditionVor der MethodenausführungEntscheidet, ob Caching überhaupt greifen soll
unlessNach der MethodenausführungEntscheidet, ob das Ergebnis gecacht wird

Das ist wichtig in Szenarien wie:

  • Nur teure Lookups für gültige, wohlgeformte IDs cachen
  • Cache für anonyme oder Test‑User überspringen
  • Cache‑Verschmutzung mit null‑Ergebnissen oder Fehlerzuständen vermeiden

Auch das Key‑Design verdient Beachtung. Default‑Keys auf Basis aller Parameter reichen für einfache Fälle. Komplexere Methoden profitieren von expliziten Keys, die Kollisionen vermeiden und mit Nullwerten robust umgehen.

Das Attribut condition verwenden

condition wird vor der Ausführung ausgewertet und entscheidet, ob Caching überhaupt berücksichtigt wird. Ergibt die Bedingung false, läuft die Methode ohne Cache‑Interaktion.

@Cacheable(
    cacheNames = "books", 
    key = "#isbn", 
    condition = "#isbn != null and #isbn.length() == 13"
)
public Book getBookByIsbn(String isbn) {
    return bookRepository.findByIsbn(isbn);
}

Hier werden nur gültige 13‑stellige ISBNs gecacht. Fehlerhafte oder null‑ISBNs umgehen das Caching vollständig – kein Lookup, keine Speicherung.

Ein weiteres Beispiel basierend auf dem Kundenstatus:

@Cacheable(
    cacheNames = "pricing", 
    key = "#productId", 
    condition = "#customer.status == 'PREMIUM'"
)
public PricingDetails getPremiumPricing(Long productId, Customer customer) {
    return pricingService.calculatePremiumPrice(productId, customer);
}

condition funktioniert auch mit @CacheEvict und @CachePut. In einem Multi‑Tenant‑System könnten Sie z. B. nur für bestimmte Tenants evicten:

@CacheEvict(
    cacheNames = "tenantData", 
    key = "#tenantId", 
    condition = "#tenantId != 'system'"
)
public void updateTenantData(String tenantId, TenantConfig config) {
    // Update logic
}

Das Attribut unless verwenden

unless wird nach der Ausführung ausgewertet und kann via #result auf den Rückgabewert zugreifen. So lassen sich Entscheidungen treffen wie „nur cachen, wenn das Ergebnis nicht null ist oder eine bestimmte Größe hat“.

@Cacheable(
    cacheNames = "books", 
    key = "#isbn", 
    unless = "#result == null"
)
public Book getBookByIsbn(String isbn) {
    return bookRepository.findByIsbn(isbn);
}

So verhindern Sie das Cachen fehlender Datensätze. Ohne diese Klausel würde eine nicht gefundene ISBN null cachen – spätere Anfragen blieben null, selbst wenn das Buch später hinzugefügt wird.

Ein weiteres Beispiel abhängig von der Ergebnisgröße:

@Cacheable(
    cacheNames = "searchResults", 
    key = "#query", 
    unless = "#result.size() > 1000 or #result.isEmpty()"
)
public List<Book> searchBooks(String query) {
    return bookRepository.search(query);
}

Das vermeidet das Cachen von:

  • Leeren Ergebnissen (die ggf. nur „noch keine Treffer“ bedeuten)
  • Sehr großen Ergebnissen, die zu viel Cache‑Speicher fressen

condition und unless lassen sich kombinieren:

  • condition: „Soll Caching überhaupt versucht werden?“ (vor der Ausführung)
  • unless: „Angesichts dieses Ergebnisses: Sollen wir speichern?“ (nach der Ausführung)

Java‑basierte Cache‑Konfigurationen integrieren

Auch wenn Spring Boot vieles auto‑konfiguriert, geben Java‑Konfigurationen Ihnen präzise Kontrolle über Policies wie TTL, maximale Größe und Eviction‑Verhalten.

Das gängige Muster: Eine Konfigurationsklasse mit @Bean‑Methoden, die konfigurierte CacheManager‑Instanzen zurückgeben:

@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofMinutes(10))
            .recordStats());
        return manager;
    }
}

Mit Java‑Config können Sie pro Cache unterschiedliche Policies definieren. Beispiel:

  • „customers“-Cache mit 10‑Minuten‑TTL für häufig wechselnde Daten
  • „countries“-Cache mit 24‑Stunden‑TTL für Referenzdaten
  • „exchangeRates“-Cache mit 5‑Minuten‑TTL für externe API‑Ergebnisse

Der CacheManager verbindet Ihre annotierten Service‑Methoden mit dem tatsächlichen Speicher. Ein CustomerDataService mit @Cacheable("customers") nutzt die für diesen Cache‑Namen definierten Settings.

In‑Memory‑Cache konfigurieren (z. B. Caffeine)

Caffeine ist ein performanter lokaler Cache für Spring‑Anwendungen, die in einer einzelnen JVM laufen. Empfohlen, wenn Sie kein verteiltes Caching über mehrere Instanzen benötigen.

Fügen Sie die Caffeine‑Abhängigkeit hinzu:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

Konfigurieren Sie den CaffeineCacheManager mit Policies:

@Bean
public CacheManager cacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager("products", "categories");
    manager.setCaffeine(Caffeine.newBuilder()
        .maximumSize(10_000)
        .expireAfterWrite(Duration.ofMinutes(5))
        .recordStats());
    return manager;
}

Diese Konfiguration:

  • erstellt benannte Caches für „products“ und „categories“
  • limitiert jeden Cache auf maximal 10.000 Einträge (verhindert unnötigen Speicherverbrauch)
  • lässt Einträge 5 Minuten nach dem Schreiben ablaufen
  • erfasst Hit/Miss‑Statistiken fürs Monitoring

Für eine einfache Einrichtung bietet Spring Boot eine direkte Integration über application.yml:

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=10000,expireAfterWrite=5m

Caffeine eignet sich besonders für:

  • Single‑Node‑Deployments oder kleine Cluster
  • Niedrigste Latenzanforderungen (Sub‑Mikrosekunden‑Lookups)
  • Leseintensive Workloads mit 95%+ Hit‑Rates

Verteilten Cache konfigurieren (z. B. Redis)

Redis ist in der Produktion weit verbreitet als verteilter In‑Memory‑Datenspeicher. Es ist die erste Wahl für Caching in Microservices oder skalierten Spring Boot‑Deployments, in denen mehrere Instanzen Daten teilen müssen.

Fügen Sie Spring Data Redis hinzu:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Konfigurieren Sie die Verbindung in application.yml:

spring:
  redis:
    host: localhost
    port: 6379
    password: ${REDIS_PASSWORD:}
  cache:
    type: redis
    redis:
      time-to-live: 600s

Spring Boot auto‑konfiguriert den RedisCacheManager – oder Sie definieren Ihren eigenen Bean für pro‑Cache‑Konfigurationen:

@Bean
public RedisCacheManagerBuilderCustomizer cacheManagerCustomizer() {
    return builder -> builder
        .withCacheConfiguration("shortLived",
            RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(1)))
        .withCacheConfiguration("longLived",
            RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(24)));
}

Konkrete Einsatzszenarien für Redis‑Caching:

  • Authentifizierungstokens, die zwischen Instanzen hinter einem Load Balancer geteilt werden
  • Sitzungsähnliche Daten in stateless Deployments
  • API‑Response‑Caching für zukünftige Anfragen im gesamten Cluster
  • Feature Flags und Konfigurationen, die über Knoten hinweg konsistent sein müssen

Abwägungen:

  • Netzwerklatenz addiert 5–10 ms gegenüber In‑Process‑Caffeine
  • Erfordert Serialisierung (typisch JSON via Jackson), was Overhead bedeutet
  • Bessere Skalierbarkeit und geteilte Zustände zwischen Services
  • Redis bewältigt 100k+ Operationen pro Sekunde pro Shard

Das Bild zeigt ein Netzwerk miteinander verbundener Server und symbolisiert verteilte Systeme. Es unterstreicht die Bedeutung von Caching-Verhalten in einer Spring Boot-Anwendung und wie mehrere Caching-Annotationen die Datenabfrageeffizienz über verschiedene Cache-Provider hinweg verbessern.

Best Practices, Fallstricke und Fazit

Sie haben nun die Grundlagen von Spring Boot‑Caching kennengelernt: die Cache‑Abstraktion aktivieren, @Cacheable/@CachePut/@CacheEvict und andere Annotationen einsetzen, bedingtes Caching anwenden und Provider‑Optionen wie Caffeine und Redis konfigurieren.

Beachten Sie diese Best Practices:

  • Angemessene TTLs wählen: Frische und Performance ausbalancieren. Zu kurz bedeutet ständige Misses, zu lang birgt Stale‑Risiken
  • Hochvolatile Daten nicht cachen: Wenn sich Daten sekündlich ändern, bringt Caching mehr Komplexität als Nutzen
  • Klar benennen: Deskriptive, konsistente Cache‑Namen, die die Domänenobjekte widerspiegeln
  • Writes mit Invalidation paaren: Jede mutierende Methode sollte relevante Caches evicten oder aktualisieren
  • Hit/Miss‑Metriken monitoren: Nutzen Sie das Spring Boot Actuator‑Endpoint /actuator/caches zur Erfolgskontrolle

Häufige Fallstricke, die Sie vermeiden sollten:

FallstrickProblemLösung
SelbstaufrufEin gecachter Methodenaufruf aus derselben Klasse umgeht den ProxyÜber die injizierte Bean aufrufen oder in separate Klassen refaktorieren
Eviction vergessenWrites passieren, aber der Cache liefert alte Daten@CacheEvict zu allen Mutationsmethoden hinzufügen
Unbegrenzte CachesSpeicherauslastung wächst bis zum OOMImmer maximumSize oder Eintragslimits setzen
nulls cachenFehlende Datensätze werden unbegrenzt gehaltenunless = "#result == null" verwenden
Over‑CachingJede beliebige Methode bekommt @CacheableSelektiv cachen, wo Latenz zählt

Überzeugen Sie sich selbst mit dieser Übung:

  1. Erstellen Sie einen einfachen Endpoint wie /api/books/{isbn}, der eine langsame Servicemethode aufruft
  2. Fügen Sie der Servicemethode @Cacheable hinzu
  3. Messen Sie die Antwortzeiten vorher/nachher mit Spring Boot Actuator‑Metriken oder einem Tool wie JMeter
  4. Beobachten Sie die Logs – die Methode läuft nur bei Cache‑Misses

Die Cache‑Abstraktion von Spring bietet eine konsistente, annotationsgetriebene Schicht, die providerübergreifend funktioniert. Sie können in der Entwicklung mit einem einfachen In‑Memory‑Cache starten, im Single‑Node‑Betrieb Caffeine nutzen und beim Skalieren auf mehrere Instanzen zu Redis wechseln – ohne Änderungen am Service‑Code.

Mehr dazu finden Sie in der offiziellen Dokumentation zu Spring Boot Caching sowie in erweiterten Mustern wie Cache‑Aside mit reaktiver Unterstützung in neueren Spring Boot 3.x‑Releases.

Die populärsten Frameworks sind nicht zufällig beliebt – das Caching von Spring ist erprobt, flexibel und bereit für jede Skalierung, die Ihre Anwendung verlangt. Starten Sie klein, messen Sie den Effekt und erweitern Sie Ihre Caching‑Strategie mit wachsenden Performance‑Anforderungen.

Veröffentlicht am 10. Februar 2026

Teilen


Alexander Stasiak

CEO

Digital Transformation Strategy for Siemens Finance

Cloud-based platform for Siemens Financial Services in Poland

See full Case Study
Ad image
Spring Boot caching flow diagram showing service layer, cache, and database
Verpassen Sie nichts – abonnieren Sie unseren Newsletter
Ich stimme dem Empfang von Marketing-Kommunikation von Startup House zu. Klicken Sie für die Details

Das könnte Ihnen auch gefallen...

Ruby on Rails - guide
Digital productsJava

Ist JavaScript single-threaded? Die Grundlagen erklärt

JavaScript ist eine Single-Threaded-Programmiersprache, die jeweils nur eine Aufgabe ausführt – das vereinfacht die Entwicklung und hält Anwendungen reaktionsfähig. Dieser Artikel erklärt, wie JavaScript mithilfe asynchroner Programmierung und des Event Loops mehrere Operationen koordiniert. Diese Konzepte zu verstehen ist entscheidend, um die Performance zu optimieren und effiziente Webanwendungen zu entwickeln.

Marek Majdak

29. Apr. 20248 Min. Lesezeit

Bereit, Ihr Know-how mit KI zu zentralisieren?

Beginnen Sie ein neues Kapitel im Wissensmanagement – wo der KI-Assistent zum zentralen Pfeiler Ihrer digitalen Support-Erfahrung wird.

Kostenlose Beratung buchen

Arbeiten Sie mit einem Team, dem erstklassige Unternehmen vertrauen.

Rainbow logo
Siemens logo
Toyota logo

Wir entwickeln, was als Nächstes kommt.

Unternehmen

Startup Development House sp. z o.o.

Aleje Jerozolimskie 81

Warsaw, 02-001

VAT-ID: PL5213739631

KRS: 0000624654

REGON: 364787848

Kontakt

hello@startup-house.com

Unser Büro: +48 789 011 336

Neues Geschäft: +48 798 874 852

Folgen Sie uns

Award
logologologologo

Copyright © 2026 Startup Development House sp. z o.o.

EU-ProjekteDatenschutzerklärung