CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

82

Quality

82%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

Validation failed for skills in this tile
One or more skills have errors that need to be fixed before they can move to Implementation and Discovery review.
Overview
Quality
Evals
Security
Files

cache-core-reference.mdplugins/developer-kit-java/skills/spring-boot-cache/references/

Spring Boot Cache Abstraction - References

Complete API reference and external resources for Spring Boot caching.

Spring Cache Abstraction API Reference

Core Interfaces

CacheManager

Interface for managing cache instances.

public interface CacheManager {
    // Get a cache by name
    Cache getCache(String name);
    
    // Get all available cache names
    Collection<String> getCacheNames();
}

Common Implementations:

  • ConcurrentMapCacheManager - In-memory, thread-safe caching
  • SimpleCacheManager - Simple static cache configuration
  • CaffeineCacheManager - High-performance caching with Caffeine library
  • EhCacheManager - Enterprise caching with EhCache
  • RedisCacheManager - Distributed caching with Redis

Cache

Interface representing a single cache.

public interface Cache {
    // Get cache name
    String getName();
    
    // Get native cache implementation
    Object getNativeCache();
    
    // Get value by key
    ValueWrapper get(Object key);
    
    // Put value in cache
    void put(Object key, Object value);
    
    // Remove entry from cache
    void evict(Object key);
    
    // Clear entire cache
    void clear();
}

Cache Annotations

AnnotationPurposeTargetParameters
@CacheableCache method result before executionMethodsvalue, key, condition, unless
@CachePutAlways execute, then cache resultMethodsvalue, key, condition, unless
@CacheEvictRemove entry/entries from cacheMethodsvalue, key, allEntries, condition, beforeInvocation
@CachingCombine multiple cache operationsMethodscacheable, put, evict
@CacheConfigClass-level cache configurationClassescacheNames
@EnableCachingEnable caching supportConfiguration classesNone

Annotation Parameters

value / cacheNames

Name(s) of the cache(s) to use.

@Cacheable(value = "products")  // Single cache
@Cacheable(value = {"products", "inventory"})  // Multiple caches

key

SpEL expression to generate cache key (if not using method parameters as key).

@Cacheable(value = "products", key = "#id")
@Cacheable(value = "products", key = "#p0")  // First parameter
@Cacheable(value = "products", key = "#root.methodName + #id")
@Cacheable(value = "products", key = "T(java.util.Objects).hash(#id, #name)")

SpEL Context Variables:

  • #root.methodName - Method name being invoked
  • #root.method - Method object
  • #root.target - Target object
  • #root.targetClass - Target class
  • #root.args[0] - Method arguments array
  • #a0, #p0 - First argument
  • #result - Method result (only in @CachePut, @CacheEvict)

condition

SpEL expression evaluated before cache operation. Operation only executes if true.

@Cacheable(value = "products", condition = "#id > 0")
@Cacheable(value = "products", condition = "#price > 100 && #active == true")
@Cacheable(value = "products", condition = "#size() > 0")  // For collections

unless

SpEL expression evaluated AFTER method execution. Entry is cached only if false.

@Cacheable(value = "products", unless = "#result == null")
@CachePut(value = "products", unless = "#result.isPrivate()")

beforeInvocation

For @CacheEvict only. If true, cache is evicted BEFORE method execution (default: false).

@CacheEvict(value = "products", beforeInvocation = true)  // Evict before call
@CacheEvict(value = "products", beforeInvocation = false)  // Evict after call

allEntries

For @CacheEvict only. If true, entire cache is cleared instead of single entry.

@CacheEvict(value = "products", allEntries = true)  // Clear all entries

Configuration Reference

Maven Dependencies

<!-- Spring Cache Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>3.5.6</version>
</dependency>

<!-- Caffeine (Optional, for advanced caching) -->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.6</version>
</dependency>

<!-- EhCache (Optional, for distributed caching) -->
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.10.8</version>
    <classifier>jakarta</classifier>
</dependency>

<!-- Redis (Optional, for distributed caching) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>3.5.6</version>
</dependency>

Gradle Dependencies

dependencies {
    // Spring Cache Starter
    implementation 'org.springframework.boot:spring-boot-starter-cache:3.5.6'

    // Caffeine
    implementation 'com.github.ben-manes.caffeine:caffeine:3.1.6'

    // EhCache
    implementation 'javax.cache:cache-api:1.1.1'
    implementation 'org.ehcache:ehcache:3.10.8'

    // Redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.5.6'
}

Application Properties (application.properties)

# General Caching Configuration
spring.cache.type=simple  # Type: simple, redis, caffeine, ehcache, jcache

# Caffeine Configuration
spring.cache.caffeine.spec=maximumSize=1000,expireAfterWrite=10m
spring.cache.cache-names=products,users,orders

# Redis Configuration
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.password=
spring.cache.redis.time-to-live=600000  # 10 minutes in ms

# EhCache Configuration
spring.cache.jcache.config=classpath:ehcache.xml

Application Properties (application.yml)

spring:
  cache:
    type: simple
    cache-names:
      - products
      - users
      - orders
    
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=10m
    
    redis:
      time-to-live: 600000  # 10 minutes in ms
    
    jcache:
      config: classpath:ehcache.xml

Performance Tuning Reference

Cache Types Comparison

TypeUse CaseMemoryThread-SafeDistributed
SimpleLocal, small dataLowYesNo
CaffeineHigh-performance localMediumYesNo
EhCacheEnterprise localHighYesOptional
RedisDistributed, largeExternalYesYes

Performance Tips

1. Key Generation Strategy:

// Fast (uses method parameters directly)
@Cacheable(value = "products")  // Uses all parameters as key
@Cacheable(value = "products", key = "#id")  // Specific parameter

// Slower (computed SpEL)
@Cacheable(value = "products", key = "T(java.util.Objects).hash(#id, #name)")

2. Cache Size Tuning:

# Caffeine: Set appropriate maximumSize
spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=15m

# Redis: Monitor memory usage
# MEMORY STATS command in Redis CLI

3. TTL Configuration:

# Redis: TTL in milliseconds
spring.cache.redis.time-to-live=600000  # 10 minutes

# Caffeine: In spec
spring.cache.caffeine.spec=expireAfterWrite=10m

Spring Boot Auto-Configuration

Auto-Detected Cache Managers

Spring Boot auto-configures a CacheManager based on classpath presence (in priority order):

  1. Redis - if spring-boot-starter-data-redis is present
  2. Caffeine - if caffeine library is present
  3. EhCache - if ehcache library is present
  4. Simple - default in-memory caching

To explicitly set the cache type:

spring.cache.type=redis

Conditional Bean Creation

@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("products", "users");
}

Transaction Integration

Cache + @Transactional Interaction

@Service
@Transactional
public class ProductService {
    
    @Cacheable(value = "products", key = "#id")
    @Transactional(readOnly = true)  // Combines with cache
    public Product getProduct(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    @CachePut(value = "products", key = "#product.id")
    @Transactional  // Ensure atomicity of save + cache update
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
    
    @CacheEvict(value = "products", key = "#id")
    @Transactional
    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
}

Monitoring and Metrics

Spring Boot Actuator Integration

# Enable caching metrics
management.endpoints.web.exposure.include=metrics,health

# View cache metrics
GET http://localhost:8080/actuator/metrics
GET http://localhost:8080/actuator/metrics/cache.hits
GET http://localhost:8080/actuator/metrics/cache.misses

Custom Cache Metrics

@Component
public class CacheMetricsCollector {
    private final MeterRegistry meterRegistry;

    public void recordCacheHit(String cacheName) {
        meterRegistry.counter("cache.hits", "cache", cacheName).increment();
    }

    public void recordCacheMiss(String cacheName) {
        meterRegistry.counter("cache.misses", "cache", cacheName).increment();
    }
}

EhCache XML Configuration Reference

ehcache.xml Structure

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.ehcache.org/v3"
    xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
    xsi:schemaLocation="
        http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
        http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">

    <!-- Cache Configuration -->
    <cache alias="cacheName">
        <key-type>java.lang.Long</key-type>
        <value-type>com.example.Product</value-type>
        
        <!-- Time to Live -->
        <expiry>
            <ttl unit="minutes">30</ttl>
        </expiry>
        
        <!-- Storage Configuration -->
        <resources>
            <heap unit="entries">1000</heap>
            <offheap unit="MB">50</offheap>
            <disk unit="GB">1</disk>
        </resources>
        
        <!-- Listeners (optional) -->
        <listeners>
            <listener>
                <class>com.example.CustomCacheEventListener</class>
                <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
                <events-to-fire-on>CREATED</events-to-fire-on>
                <events-to-fire-on>EXPIRED</events-to-fire-on>
            </listener>
        </listeners>
    </cache>
</config>

Common EhCache Attributes

  • heap - On-heap memory storage (fast, limited)
  • offheap - Off-heap memory storage (slower, larger)
  • disk - Disk storage (slowest, unlimited)
  • ttl - Time to live before expiration
  • idle - Time to idle before expiration (if not accessed)

Common Pitfalls and Solutions

Problem 1: Cache Not Working

Symptoms: Cache is never hit, always querying database.

Causes & Solutions:

// Problem: @Cacheable on public method called from same bean
@Service
public class ProductService {
    @Cacheable("products")
    public Product get(Long id) { }
    
    public Product getDetails(Long id) {
        return this.get(id);  // ❌ Won't use cache (no proxy)
    }
}

// Solution: Inject service or call through interface
@Service
public class DetailsService {
    @Autowired
    private ProductService productService;
    
    public Product getDetails(Long id) {
        return productService.get(id);  // ✅ Uses cache
    }
}

// Problem: Caching non-serializable objects with Redis
@Cacheable("products")
public Product get(Long id) {
    Product p = new Product();
    p.setConnection(dbConnection);  // ❌ Not serializable
    return p;
}

// Solution: Ensure all cached objects are serializable
@Cacheable("products")
public ProductDTO get(Long id) {
    return mapper.toDTO(productRepository.findById(id));  // ✅ DTO is serializable
}

Problem 2: Stale Cache Data

Symptoms: Updates aren't reflected in cached data.

Solution:

// Always evict cache on update
@CacheEvict(value = "products", key = "#id")
public void updateProduct(Long id, UpdateRequest req) {
    Product product = productRepository.findById(id).orElseThrow();
    product.update(req);
    productRepository.save(product);
}

// Or use @CachePut to keep cache fresh
@CachePut(value = "products", key = "#result.id")
public Product updateProduct(Long id, UpdateRequest req) {
    Product product = productRepository.findById(id).orElseThrow();
    product.update(req);
    return productRepository.save(product);
}

Problem 3: Memory Leak

Symptoms: Memory usage grows unbounded.

Solution:

# Configure cache eviction policies
spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=10m

# Redis: Set TTL
spring.cache.redis.time-to-live=600000

# Monitor cache size

External Resources

Official Documentation

  • Spring Cache Abstraction
  • Spring Boot Caching Documentation
  • Spring Framework Caching Guide

Third-Party Libraries

Related Skills

  • spring-boot-performance-tuning - Comprehensive performance optimization
  • spring-boot-data-persistence - Database optimization patterns
  • spring-boot-rest-api-standards - API design with caching headers

Useful Articles

SpEL Reference for Cache Keys

Basic Expressions

// Method parameters
@Cacheable(key = "#id")           // Single parameter
@Cacheable(key = "#user.id")      // Object property
@Cacheable(key = "#root.args[0]") // First argument

// Composite keys
@Cacheable(key = "#id + '-' + #type")
@Cacheable(key = "T(java.util.Objects).hash(#id, #type)")

// Collections
@Cacheable(key = "#ids.toString()")
@Cacheable(condition = "#ids.size() > 0")

SpEL Context Variables

VariableDescription
#root.methodMethod object
#root.methodNameMethod name
#root.targetTarget object
#root.targetClassTarget class
#root.argsArguments array
#p<index>Argument at index
#<name>Named argument
#resultMethod result (@CachePut, @CacheEvict)

Testing Reference

Testing Cache Behavior

@Test
void shouldCacheResult() {
    // Arrange
    when(repository.find(1L)).thenReturn(mockObject);
    
    // Act - First call
    service.get(1L);
    
    // Assert - Database was queried
    verify(repository, times(1)).find(1L);
    
    // Act - Second call
    service.get(1L);
    
    // Assert - Database NOT queried again (cache hit)
    verify(repository, times(1)).find(1L);
}

Disabling Cache in Tests

@SpringBootTest
@PropertySource("classpath:application-test.properties")
class MyServiceTest {
    // In application-test.properties:
    // spring.cache.type=none
}

plugins

developer-kit-java

skills

README.md

tile.json