Caching is a powerful technique that can drastically improve application performance by reducing the need to retrieve data from a slow data store like a database. JetCache is a Java-based caching library that integrates seamlessly with Spring Boot, providing annotations, easy configuration, and multi-level caching support (local + remote). In this article, we will explore how to use JetCache in a Spring Boot application, covering its key features and practical examples.
Why JetCache?
JetCache stands out for the following reasons:
-
Ease of Use: It provides simple annotations like
@Cached
for caching methods. - Support for Multi-Level Caching: You can configure both local (in-memory) and remote (Redis) caches easily.
- Auto Configuration with Spring Boot: JetCache integrates easily with Spring Boot's auto-configuration features.
- Out-of-the-Box Integration: It supports various cache providers such as Redis, Caffeine, and even custom implementations.
Key Features of JetCache
-
Declarative Caching: Use annotations like
@Cached
,@CacheInvalidate
, and@CacheUpdate
for caching behavior. - Multi-Level Cache: It allows using both local and remote cache providers for efficient caching strategies.
- Support for Key Generation: JetCache provides automatic or custom key generation for cache entries.
- TTL (Time to Live) Settings: Set expiration time for cache entries on both local and remote levels.
Let’s now dive into how to integrate JetCache into a Spring Boot application.
Step 1: Set Up the Spring Boot Project
To get started, create a new Spring Boot project with the necessary dependencies. You can use Spring Initializr or manually add the following dependencies to your pom.xml
:
<dependency>
<groupId>com.github.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis-lettuce</artifactId> <!-- For Redis support -->
<version>2.7.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <!-- For Local Cache -->
</dependency>
Step 2: Configure JetCache in application.yml
spring:
cache:
type: none
jetcache:
statIntervalMinutes: 5 # Statistics output interval
areaInCacheName: false
local:
default:
limit: 100 # Maximum number of entries in local cache
expireAfterWriteInMillis: 60000 # TTL for local cache
remote:
default:
type: redis
keyConvertor: fastjson
ttlInMillis: 300000 # 5 minutes TTL for Redis cache
jedis:
host: localhost
port: 6379
password: "" # Add password if Redis requires authentication
Step 3: Create a Service with Caching Enabled
Let’s create a simple service that fetches user data, simulating a database query, and cache the result.
User Entity
public class User {
private Long id;
private String name;
// Constructor, Getters, and Setters
public User(Long id, String name) {
this.id = id;
this.name = name;
}
// getters and setters
}
UserService
import com.alicp.jetcache.anno.Cached;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// Simulate a database call
@Cached(name = "userCache-", key = "#id", expire = 300, localExpire = 60)
public User getUserById(Long id) {
simulateSlowService();
return new User(id, "User " + id);
}
// Invalidate the cache entry for the given ID
@CacheInvalidate(name = "userCache-", key = "#id")
public void invalidateUserCache(Long id) {
// This method will remove the user from the cache
}
// Simulate a slow response (e.g., a database call)
private void simulateSlowService() {
try {
Thread.sleep(3000); // Simulate delay
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
In this service:
- The
@Cached
annotation is used to cache the result ofgetUserById
. The key for the cache is theid
, and the cache entry will expire after 5 minutes in Redis and 1 minute in local memory. - The
@CacheInvalidate
annotation allows us to remove a specific entry from the cache.
Step 4: Use the Service in a Controller
Let’s expose this service via a REST controller to fetch and invalidate cached user data.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping("/user/{id}/invalidate")
public String invalidateCache(@PathVariable Long id) {
userService.invalidateUserCache(id);
return "Cache for User ID " + id + " invalidated.";
}
}
Step 5: Test the Caching Behavior
When you run the application, access the following endpoints:
- GET /user/1: The first call will simulate a slow service and take 3 seconds to return. The result will be cached.
- GET /user/1 (subsequent call): This call will return the cached result instantly from the local cache (if within 1 minute) or from Redis (if within 5 minutes).
-
GET /user/1/invalidate: This endpoint invalidates the cached data for the user with ID
1
.
Step 6: Monitor the Cache
JetCache provides built-in statistics to monitor cache hits and misses. By default, it logs cache statistics every 5 minutes. You can configure this interval or manually output statistics using CacheMonitor
.
JetCache Annotations
Let’s dive deeper into some of the key JetCache annotations:
-
@Cached
: Caches the result of a method. Parameters:-
name
: Prefix for cache key names. -
key
: SpEL expression to specify cache keys. -
expire
: TTL for remote cache in seconds. -
localExpire
: TTL for local cache in seconds.
-
-
@CacheInvalidate
: Removes an entry from the cache. Parameters:-
name
: Prefix for the cache key name. -
key
: SpEL expression to specify the cache key to invalidate.
-
@CacheUpdate
: Updates an existing cache entry. You can use this for methods that modify data and should refresh the cache.
Multi-Level Caching with JetCache
JetCache supports both local and remote caches. Here’s a quick summary of how multi-level caching works:
- Local Cache: Stored in memory (e.g., using Caffeine). Local cache is typically faster but has limited size.
- Remote Cache: Stored in external services like Redis. Remote caches are slower but can store much larger datasets.
JetCache uses local cache first. If a cache miss occurs, it will check the remote cache (e.g., Redis). This setup offers a good balance between speed and capacity.
Best Practices for Using JetCache
- Avoid Over-Caching: Cache only data that is expensive to retrieve and doesn’t change frequently.
- Set Reasonable Expirations: Choose appropriate TTLs for both local and remote caches to avoid stale data.
- Monitor Cache Usage: JetCache provides statistics to help you monitor cache performance. Use them to optimize your cache settings.
- Consider Cache Size: Especially for local caches, ensure you have limits in place to avoid memory overflow.
Conclusion
JetCache provides a simple yet powerful way to integrate caching into your Spring Boot application. With easy-to-use annotations, support for multi-level caching, and robust integration with popular cache providers like Redis, JetCache can significantly improve your application's performance.
By following the steps in this guide, you can set up caching in your Spring Boot applications quickly and efficiently. Whether you’re caching small lookups in local memory or large datasets in Redis, JetCache has you covered.
Further Reading: