1. Caching Fundamentals
Caching is essentially storing frequently used data in a temporary storage location (cache) close to your application so that it can be accessed much faster compared to fetching it from a slower source such as:
- A relational or NoSQL database
- A remote API
- A file system
1.1 Why Caching Matters
- Performance boost: Reduces latency by serving data directly from memory.
- Reduced load: Minimizes database or API calls, lowering server load.
- Improved scalability: Frees up resources so your application can serve more users simultaneously.
- Cost efficiency: In cloud environments, reducing API/database calls can reduce operational costs.
1.2 Key Caching Operations in Any Application
When implementing caching in Spring Boot, you usually perform the following actions:
1.2.1 Enable caching in the application
This step allows your application to detect and use caching annotations like @Cacheable
, @CachePut
, and @CacheEvict
. Without enabling caching, annotations will have no effect.
Enabling caching is typically done at the configuration or main application class level.
1.2.2 Store data in the cache
When you mark a method as cacheable, the data returned from that method will be stored in the cache for future retrievals. This reduces repetitive method execution.
1.2.3 Update the cache
When the underlying data changes (for example, a database row is updated), you need to refresh the cache so it reflects the latest state. This avoids stale or outdated results.
1.2.4 Evict (remove) data from the cache
Sometimes, you need to remove cache entries entirely — for example, after a delete operation or when certain data becomes irrelevant.
2. Spring Boot Cache Abstraction
Spring Boot’s caching system is not a cache implementation by itself. Instead, it provides an abstraction layer that sits on top of various cache providers.
Think of it as:
- Spring Cache abstraction = A common interface for interacting with different caching technologies.
- Cache providers = Actual implementations such as Ehcache, Caffeine, Redis, etc.
This means you can start with a simple in-memory cache (ConcurrentMap) and later switch to Redis or Caffeine without changing your caching-related code — just change the configuration and dependency.
3. Cache Annotations in Spring Boot
Spring Boot provides a set of annotations to manage caching behavior. Let’s go through them one by one.
3.1 @EnableCaching
Purpose: Activates annotation-driven cache management in Spring Boot.
Where to use:
Usually placed on your main Spring Boot application class or a configuration class.
@SpringBootApplication @EnableCaching public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
Detail:
Without @EnableCaching
, none of the caching annotations (@Cacheable
, @CachePut
, @CacheEvict
) will take effect. It tells Spring to scan and interpret these annotations during method calls.
3.2 @Cacheable
Purpose: Caches the result of a method execution.
If the same method is called again with the same parameters, the cached result is returned instead of executing the method.
When to use:
- Database lookups
- Expensive API calls
- Computations that don’t change frequently
@Cacheable(cacheNames = "books") public Book findBook(String isbn) { System.out.println("Fetching from database..."); return bookRepository.findByIsbn(isbn); }
Detailed Flow:
- When the method is called, Spring checks if a value for the given key already exists in the specified cache.
- If found → returns cached value immediately (skips method execution).
- If not found → executes the method, stores the result in the cache, and returns it.
3.2.1 Defining Cache Keys
By default, Spring generates a cache key based on all method parameters.
You can define a custom key using Spring Expression Language (SpEL):
@Cacheable(cacheNames = "books", key = "#isbn") public Book findBook(String isbn) { ... }
You can even use object properties:
@Cacheable(cacheNames = "books", key = "#book.isbn") public Book findBookByBookObject(Book book) { ... }
3.2.2 Conditional Caching
You can choose to cache data only when certain conditions are met:
@Cacheable(cacheNames = "books", condition = "#name.length() < 32") public Book findBookByName(String name) { ... }
3.2.3 Synchronized Caching
Avoids multiple threads caching the same key at the same time:
@Cacheable(cacheNames = "books", key = "#isbn", sync = true) public Book findBook(String isbn) { ... }
3.3 @CachePut
Purpose: Always executes the method and updates the cache with the result.
Purpose: Always executes the method and updates the cache with the result.
When to use:
- Updating cache after a database update.
- Ensuring cache always has the latest value.
@CachePut(cacheNames = "books", key = "#book.isbn") public Book updateBook(Book book) { return bookRepository.save(book); }
Difference from @Cacheable
:
@Cacheable
: May skip method execution if value is cached.@CachePut
: Always executes and updates the cache.
3.4 @CacheEvict
Purpose: Removes entries from the cache.
When to use:
- After deleting a record from the database.
- When data becomes outdated or irrelevant.
@CacheEvict(cacheNames = "books", key = "#isbn") public void deleteBook(String isbn) { bookRepository.deleteByIsbn(isbn); }
Clear entire cache:
@CacheEvict(cacheNames = "books", allEntries = true) public void clearAllBooksCache() { }
3.5 @Caching
Purpose: Combines multiple caching annotations (@Cacheable
, @CachePut
, @CacheEvict
) on the same method.
@Caching( evict = { @CacheEvict(cacheNames = "primary", key = "#book.id"), @CacheEvict(cacheNames = "secondary", key = "#book.isbn") } ) public void removeBook(Book book) { ... }
3.6 @CacheConfig
Purpose: Defines common cache settings at the class level to avoid repetition.
@CacheConfig(cacheNames = "books") public class BookService { @Cacheable(key = "#isbn") public Book findBook(String isbn) { ... } @CachePut(key = "#book.isbn") public Book updateBook(Book book) { ... } }
4. Cache Providers in Spring Boot
Spring Boot supports multiple caching implementations through its abstraction layer.
Some commonly used providers are:
- ConcurrentMap – Default, simple in-memory cache, good for development and small-scale use.
- Ehcache – Java-based, feature-rich, widely used in enterprise applications.
- Caffeine – High-performance Java 8-based caching library with powerful eviction policies.
- Redis – In-memory data store, suitable for distributed caching and high scalability.
- Hazelcast / Infinispan – Distributed in-memory data grids for large-scale applications.
- JCache (JSR-107) – Standard caching API for Java.
Note: You can start with the default ConcurrentMap
and switch to something like Redis without changing caching code — just change dependencies and configuration.
5. Eviction Policies and TTL
Eviction policy determines how and when cached data is removed.
Common policies:
- TTL (Time-To-Live): Remove entries after a fixed time duration.
- Max size: Limit the number of entries; remove oldest when full.
- LRU (Least Recently Used): Remove the least recently accessed entry.
- LFU (Least Frequently Used): Remove the entry with the fewest accesses.
Example in Caffeine:
spring: cache: caffeine: spec: maximumSize=100,expireAfterWrite=5m
Important: These policies are set at the cache provider level, not in Spring’s annotations. Spring only provides the abstraction.
6. Simple Cache Manager for Development
By default:
- ConcurrentMapCacheManager is used if no other cache provider is present.
- This stores data in a simple in-memory map (non-distributed, no eviction policies).
@Service public class BookService { @Cacheable(value = "books", key = "#isbn") public Book getBookByIsbn(String isbn) { simulateSlowService(); return new Book(isbn, "Sample Book"); } private void simulateSlowService() { try { Thread.sleep(3000L); } catch (InterruptedException e) { throw new IllegalStateException(e); } } }
First call takes ~3s, subsequent calls return instantly from cache.
7. Key Takeaways
- Spring Boot’s caching system is an abstraction — actual storage is provided by different cache providers.
@EnableCaching
is required to activate annotation-driven caching.- Use
@Cacheable
for reading and storing,@CachePut
for updates, and@CacheEvict
for removals. @Caching
allows combining multiple caching annotations, and@CacheConfig
reduces repetitive configurations.- Eviction and TTL policies are provider-specific.