Caching is a crucial technique for improving the performance and scalability of applications by storing frequently accessed data in memory, reducing the need for repetitive database or API calls. Spring’s caching abstraction simplifies the use of caching in applications by providing a consistent API for various caching providers. In this blog post, we will explore the core concepts of Spring caching, provide practical examples using different caching providers, and discuss various use cases for using Spring caching in application development.
Overview of Supported Cache Providers
Spring supports various caching providers, each with its unique features and use cases:
- SimpleCache: A basic, concurrent map-based cache that is suitable for simple, local caching requirements.
- EhCache: A robust, widely-used caching solution that supports both in-memory and disk-based caching, ideal for local caching.
- Redis: An advanced key-value store that supports distributed caching, making it suitable for scenarios requiring shared cache across multiple instances.
- Hazelcast: An in-memory data grid that supports distributed caching and clustering, ideal for large-scale, high-performance caching requirements.
- Caffeine: A high-performance, in-memory caching library that provides near-optimal hit rates and supports automatic eviction policies.
Understanding Spring Caching Concepts
- @Cacheable Annotation: Marks a method for caching. The result of the method execution will be stored in the cache.
- @CachePut Annotation: Updates the cache with the method result without interfering with the method execution.
- @CacheEvict Annotation: Removes entries from the cache.
- @EnableCaching Annotation: Enables Spring’s caching capability.
- CacheManager Interface: Provides cache operations.
- CacheResolver Interface: Allows for custom cache resolution logic.
Example 1: Simple Cache
- Add Dependencies: Ensure you have the necessary dependencies in your
pom.xml
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- Enable Caching in Configuration
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
}
- Create a Simple Caching Service
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class SimpleDataService {
@Cacheable("simpleCache")
public String getData(String param) {
simulateSlowService();
return "Data for " + param;
}
private void simulateSlowService() {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
- Test the Simple Caching Service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements CommandLineRunner {
@Autowired
private SimpleDataService dataService;
@Override
public void run(String... args) throws Exception {
System.out.println("Calling getData first time:");
System.out.println(dataService.getData("test"));
System.out.println("Calling getData second time:");
System.out.println(dataService.getData("test"));
}
}
Example 2: Caching with EhCache
- Add Dependencies: Ensure you have the necessary dependencies in your
pom.xml
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
- Configure EhCache
Create an ehcache.xml
file in your resources directory.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">
<cache alias="defaultCache">
<expiry>
<ttl unit="seconds">3600</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache>
</config>
- Configure EhCache in Application
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.jcache.JCacheCacheManager;
import javax.cache.Caching;
import javax.cache.spi.CachingProvider;
@Configuration
@EnableCaching
public class EhCacheConfig {
@Bean
public CacheManager cacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager(
getClass().getResource("/ehcache.xml").toURI(),
getClass().getClassLoader());
return new JCacheCacheManager(cacheManager);
}
}
-
Create a Caching Service (Same as Simple Cache)
-
Test the Caching Service (Same as Simple Cache)
Example 3: Caching with Redis
- Add Dependencies: Ensure you have the necessary dependencies in your
pom.xml
.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- Configure Redis in Application Properties
spring.redis.host=localhost
spring.redis.port=6379
- Configure Redis Cache
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.redis.RedisCacheConfiguration;
import org.springframework.cache.redis.RedisCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
-
Create a Caching Service (Same as Simple Cache)
-
Test the Caching Service (Same as Simple Cache)
Example 4: Caching with Hazelcast
- Add Dependencies: Ensure you have the necessary dependencies in your
pom.xml
.
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- Configure Hazelcast
import com.hazelcast.config.Config;
import com.hazelcast.config.JoinConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.cache.hazelcast.HazelcastCacheManager;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
@Configuration
@EnableCaching
public class HazelcastConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
JoinConfig joinConfig = config.getNetworkConfig().getJoin();
joinConfig.getMulticastConfig().setEnabled(false);
joinConfig.getTcpIpConfig().setEnabled(true).addMember("127.0.0.1");
return Hazelcast.newHazelcastInstance(config);
}
@Bean
public CacheManager cacheManager() {
return new HazelcastCacheManager(hazelcastInstance());
}
}
-
Create a Caching Service (Same as Simple Cache)
-
Test the Caching Service (Same as Simple Cache)
Use Cases for Spring Caching in Application Development
- Database Query Caching: Cache the results of expensive database queries to improve performance.
- Example: Cache the results of a frequently accessed report query.
- API Response Caching: Cache responses from external APIs to reduce latency and API costs.
- Example: Cache weather data fetched from a third-party API.
- Session Management: Store user session data in a cache for quick retrieval.
- Example: Use Redis to manage user sessions in a web application.
- **Configuration
Caching**: Cache configuration settings that are read frequently but change infrequently.
- Example: Cache application settings loaded from a configuration file or database.
- Content Caching: Cache static content such as HTML fragments, images, or documents.
- Example: Cache the rendered output of a frequently accessed web page.
- Distributed Caching: Use a distributed cache to share data across multiple instances of an application.
- Example: Use Redis or Hazelcast to share user authentication tokens across multiple application servers.
Choosing the Right Caching Provider
- SimpleCache: Suitable for simple, local caching requirements with minimal configuration.
- EhCache: Suitable for in-process caching, easy to set up, and supports local caching.
- Redis: Distributed cache, suitable for applications requiring shared cache across multiple instances, supports persistence.
- Hazelcast: Distributed in-memory data grid, supports clustering and distributed computing.
- Caffeine: High-performance in-memory cache, ideal for scenarios requiring low latency.
Conclusion
Spring’s caching abstraction provides a powerful and flexible solution for improving application performance and scalability. By leveraging Spring’s caching capabilities, you can easily manage cache entries, enhance data retrieval speed, and reduce load on databases and external APIs.
Start incorporating Spring caching in your application development today to take advantage of these powerful features. Happy coding!