您现在的位置是:首页 >学无止境 >26、深入理解 Spring 的 @Cacheable 注解:提升系统性能的利器网站首页学无止境
26、深入理解 Spring 的 @Cacheable 注解:提升系统性能的利器
什么是 @Cacheable 注解
@Cacheable
是 Spring 框架中专门用于缓存方法返回值的注解。当一个被 @Cacheable
注解标注的方法被调用时,Spring 会首先对缓存进行检查,查看是否已经存在该方法调用所对应的结果。如果缓存中存在相应结果,Spring 会直接从缓存中获取并返回,而不会执行方法体中的代码;如果缓存中不存在该结果,Spring 则会执行方法体,并将方法的返回值存入缓存,以便后续相同的方法调用能够直接从缓存中获取结果。
这种缓存机制在实际应用中具有显著的优势。以电商系统为例,商品信息的查询操作可能会被频繁调用。如果每次查询都直接从数据库中获取数据,不仅会增加数据库的压力,还会导致查询时间变长。而使用 @Cacheable
注解,将商品信息缓存起来,下次查询时直接从缓存中获取,能够大大提高查询效率,减少用户等待时间。
基本使用方法
1. 配置缓存管理器
在使用 @Cacheable
注解之前,我们需要配置一个合适的缓存管理器。Spring 框架支持多种不同类型的缓存管理器,每种管理器都有其独特的特点和适用场景。下面我们将详细介绍几种常见的缓存管理器及其配置方法。
ConcurrentMapCacheManager
ConcurrentMapCacheManager
是一个基于 ConcurrentHashMap
的简单缓存管理器,它适用于开发和测试环境,或者对缓存要求不高的场景。在 Spring Boot 项目中,我们可以通过以下配置类来使用它:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public ConcurrentMapCacheManager cacheManager() {
return new ConcurrentMapCacheManager("myCache");
}
}
在上述代码中,@EnableCaching
注解用于启用 Spring 的缓存功能,ConcurrentMapCacheManager
构造函数中的 "myCache"
是缓存的名称。
EhCacheCacheManager
EhCache
是一个开源的、高性能的 Java 缓存框架,EhCacheCacheManager
则是 Spring 对 EhCache
的集成。使用 EhCache
可以提供更丰富的缓存配置选项,如缓存过期策略、缓存大小限制等。以下是配置 EhCacheCacheManager
的示例:
首先,需要在 pom.xml
中添加 EhCache
的依赖:
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
然后,创建 ehcache.xml
配置文件,示例内容如下:
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"/>
<cache name="myCache"
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"/>
</ehcache>
最后,在配置类中配置 EhCacheCacheManager
:
import net.sf.ehcache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public EhCacheCacheManager cacheManager() {
InputStream inputStream = getClass().getResourceAsStream("/ehcache.xml");
CacheManager ehCacheManager = CacheManager.create(inputStream);
return new EhCacheCacheManager(ehCacheManager);
}
}
RedisCacheManager
Redis
是一个开源的、高性能的键值对存储数据库,RedisCacheManager
是 Spring 对 Redis
的集成。使用 Redis
作为缓存可以实现分布式缓存,适用于多节点的应用场景。以下是配置 RedisCacheManager
的示例:
首先,在 pom.xml
中添加 Redis
的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后,在 application.properties
中配置 Redis
连接信息:
spring.redis.host=localhost
spring.redis.port=6379
最后,在配置类中配置 RedisCacheManager
:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
}
2. 使用 @Cacheable 注解
在配置好缓存管理器之后,我们就可以在需要缓存结果的方法上添加 @Cacheable
注解了。示例如下:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Cacheable("myCache")
public Product getProductById(Long id) {
// 模拟从数据库中查询商品信息
System.out.println("Querying product from database with id: " + id);
return new Product(id, "Product Name");
}
}
在上述代码中,@Cacheable("myCache")
表示将 getProductById
方法的返回值缓存到名为 "myCache"
的缓存中。当多次调用该方法并传入相同的 id
时,只有第一次会执行方法体中的代码,后续调用会直接从缓存中获取结果。
@Cacheable 注解的属性
@Cacheable
注解有多个属性,通过合理配置这些属性,我们可以根据不同的业务需求实现更加灵活和高效的缓存策略。
1. value
或 cacheNames
这两个属性用于指定缓存的名称,可以是一个或多个。例如:
@Cacheable(value = {"cache1", "cache2"})
public Product getProduct(Long id) {
// ...
}
2. key
该属性用于指定缓存的键,默认情况下,Spring 会根据方法的参数自动生成键。我们可以使用 SpEL(Spring Expression Language)表达式自定义键,例如:
@Cacheable(value = "myCache", key = "#id")
public Product getProductById(Long id) {
// ...
}
上述代码中,#id
表示使用方法的 id
参数作为缓存的键。
3. condition
此属性用于指定缓存的条件,只有当条件为 true
时才会进行缓存。例如:
@Cacheable(value = "myCache", condition = "#id > 0")
public Product getProductById(Long id) {
// ...
}
上述代码中,只有当 id
大于 0 时才会将方法的返回值缓存起来。
4. unless
该属性用于指定不进行缓存的条件,当条件为 true
时,不会将方法的返回值缓存起来。例如:
@Cacheable(value = "myCache", unless = "#result == null")
public Product getProductById(Long id) {
// ...
}
上述代码中,如果方法的返回值为 null
,则不会将其缓存。
高级应用技巧
1. 多级缓存
在实际应用中,我们可以结合不同的缓存管理器实现多级缓存。例如,将本地缓存(如 ConcurrentMapCacheManager
)和分布式缓存(如 RedisCacheManager
)结合使用。先从本地缓存中查找数据,如果找不到再从分布式缓存中查找,最后再从数据库中获取数据。这样可以进一步提高系统的性能和响应速度。以下是一个简单的多级缓存示例:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Cacheable(value = {"localCache", "redisCache"}, key = "#id")
public Product getProductById(Long id) {
// 模拟从数据库中查询商品信息
System.out.println("Querying product from database with id: " + id);
return new Product(id, "Product Name");
}
}
2. 缓存更新和删除
@Cacheable
注解主要用于缓存方法的返回值,对于缓存的更新和删除,我们可以使用 @CachePut
和 @CacheEvict
注解。@CachePut
注解用于更新缓存,无论缓存中是否已经存在该键,都会执行方法体并将返回值存入缓存中;@CacheEvict
注解用于删除缓存,可以指定删除某个或多个缓存项。示例如下:
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Cacheable("myCache")
public Product getProductById(Long id) {
// 模拟从数据库中查询商品信息
System.out.println("Querying product from database with id: " + id);
return new Product(id, "Product Name");
}
@CachePut("myCache")
public Product updateProduct(Product product) {
// 模拟更新数据库中的商品信息
System.out.println("Updating product in database with id: " + product.getId());
return product;
}
@CacheEvict("myCache")
public void deleteProduct(Long id) {
// 模拟从数据库中删除商品信息
System.out.println("Deleting product from database with id: " + id);
}
}
总结
@Cacheable
注解是 Spring 框架中一个非常实用的功能,它为我们提供了一种简单而高效的方式来实现方法结果的缓存,从而减少重复计算和数据库查询,提高系统的性能和响应速度。通过合理配置 @Cacheable
注解的属性,我们可以根据不同的业务需求实现更加灵活和高效的缓存策略。同时,结合不同类型的缓存管理器(如 ConcurrentMapCacheManager
、EhCacheCacheManager
和 RedisCacheManager
),我们可以满足不同场景下的缓存需求。此外,利用 @CachePut
和 @CacheEvict
注解,我们可以实现缓存的更新和删除操作,确保缓存数据的一致性。在实际开发中,我们应该根据系统的特点和性能需求,合理使用 @Cacheable
注解及其相关功能,为系统的性能优化提供有力支持。