Mastering Spring Cache Annotations: A Guide to @Cacheable and @CacheEvict in Spring Boot
Caching is a very useful technique that can boost the performance of your Spring Boot applications by reducing the time taken to retrieve frequently accessed data. In this blog post, we’ll see how we can use Spring Cache annotations such as @Cacheable and @CacheEvict to manage cache eviction effectively. We’ll walk through the examples that will help you implement these annotations in your application and understand how to use them.
![]() |
@Cacheable and @CacheEvict Spring Boot |
What is Caching?
Caching is the process of storing copies of files or data in a temporary storage area (called the cache) so that future requests for that data can be served much faster. In web applications, caching helps to reduce the load on the database and improves user response times, giving a better experience to the user.
Spring Cache Abstraction
Spring provides a caching abstraction that makes it easy to add caching to your applications. The Spring Cache abstraction is designed to support a wide variety of caching providers, including EhCache, Hazelcast, Caffeine, and more, so you can pick the caching provider that's right for you.
Getting Started with Spring Cache
To get started with caching in a Spring Boot application, you need to follow these steps:
- Add Dependencies: Ensure you have the necessary dependencies in your
pom.xml
orbuild.gradle
file. For example, if you are using Maven, add the following dependency for Spring Boot Starter Cache:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- Enable Caching: You need to enable caching in your Spring Boot application by adding the
@EnableCaching
annotation to your main application class.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Using @Cacheable
The @Cacheable annotation specifies that the return value of a method should be cached. Spring then checks to see if the result is already in the cache, and if so, returns the cached value. Otherwise, it executes the method, and stores the result in the cache for subsequent calls.
Example of @Cacheable
Let's create a simple service that retrieves user information from a database. We will cache the results of the getUser ById
method.
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// Simulating a database call
public User getUser ById(Long id) {
simulateSlowService(); // Simulate a slow service
return new User(id, "User " + id);
}
@Cacheable("users")
public User getCachedUser ById(Long id) {
return getUser ById(id);
}
// Simulate a slow service
private void simulateSlowService() {
try {
Thread.sleep(3000); // Simulate delay
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
In this example, the getCachedUserById method is annotated with the @Cacheable("users") annotation, so its results will be cached with the key "users". The first time you call this method with a given user ID, it takes 3 seconds to return the result. But any subsequent calls with the same user ID will return the cached result almost immediately, giving a significant performance boost.
Using @CacheEvict
The @CacheEvict
annotation is used to remove entries from the cache. This is particularly useful when you want to invalidate the cache after an update or deletion operation.
Example of @CacheEvict
Let's extend our UserService
to include a method for updating user information. We will use @CacheEvict
to clear the cache when a user is updated.
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// Existing methods...
@CacheEvict(value = "users", key = "#user.id")
public User updateUser (User user) {
// Update user in the database (simulated)
return user; // Return updated user
}
}
In this example, the updateUser
method is annotated with @CacheEvict(value = "users", key = "#user.id")
. This means that when a user is updated, the corresponding entry in the cache will be removed, ensuring that the next call to getCachedUser ById
will fetch the updated user information from the database.
Complete Example
Here’s how you can put everything together in a complete Spring Boot application.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
@SpringBootApplication
@EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
private final UserService userService;
public MyApplication(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public User getUser (@PathVariable Long id) {
return userService.getCachedUser ById(id);
}
@PutMapping
public User updateUser (@RequestBody User user) {
return userService.updateUser (user);
}
}
@Service
class UserService {
public User getUser ById(Long id) {
simulateSlowService(); // Simulate a slow service
return new User(id, "User " + id);
}
@Cacheable("users")
public User getCachedUser ById(Long id) {
return getUser ById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public User updateUser (User user) {
// Update user in the database (simulated)
return user; // Return updated user
}
private void simulateSlowService() {
try {
Thread.sleep(3000); // Simulate delay
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
class User {
private Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
// Getters and setters
}
Conclusion
In this blog post, we learned how to use the @Cacheable and @CacheEvict annotations in a Spring Boot application to manage caching. Using caching can significantly improve the performance of your application, especially for frequently accessed data. Be sure to choose the right caching provider for your application's needs and always test your caching strategy to ensure it meets your performance goals. Happy coding!