Spring Cache 提供了一种灵活而强大的缓存抽象,使得在应用中集成缓存变得更加容易。让我们从一些基本概念开始,然后深入讨论 Spring Cache 的用法和配置。
@Cacheable
、@CachePut
、@CacheEvict
等。这些注解允许你控制方法的缓存行为,如何从缓存中读取、写入和清除数据。CacheManager
是 Spring Cache 的核心接口,负责创建和管理缓存实例。Spring Cache 提供了默认的 ConcurrentMapCacheManager
,同时也支持集成其他缓存提供者,如 Ehcache、Caffeine、Redis 等。让我们通过一个简单的示例来演示 Spring Cache 的基本使用。假设有一个服务类,其中包含一个根据用户ID获取用户信息的方法,我们希望将用户信息缓存起来:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 模拟从数据库或其他数据源获取用户信息的操作
System.out.println("Fetching user info for userId: " + userId);
return "User info for userId: " + userId;
}
}
在上面的示例中,@Cacheable
注解表示 getUserInfo
方法的结果应该被缓存,缓存的名称为 “userCache”,键为方法的参数 userId
。如果多次调用 getUserInfo
方法并传递相同的 userId
,则只有第一次会执行方法体,后续的调用将直接从缓存中获取结果。
在Spring中,配置常见的缓存管理器通常涉及使用Spring Cache抽象,并选择适当的缓存提供者。以下是一些常见的缓存管理器配置示例,包括Ehcache、Caffeine、Redis等。
首先,确保项目中引入了Ehcache的相关依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
然后,在Spring Boot的配置文件中添加Ehcache的配置,比如ehcache.xml
:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd">
<defaultCache
maxElementsInMemory="100"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
/>
ehcache>
然后,在Spring Boot的配置类中配置Ehcache的CacheManager
:
import org.springframework.cache.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;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}
@Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
factoryBean.setShared(true);
return factoryBean;
}
}
添加以下依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>com.github.ben-manes.caffeinegroupId>
<artifactId>caffeineartifactId>
dependency>
接下来,在 Spring Boot 项目的配置类中配置 Caffeine Cache 作为缓存管理器:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
private Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存失效时间为10分钟
.maximumSize(100); // 设置缓存的最大条目数
}
}
在上述配置中,CaffeineCacheManager
被配置为缓存管理器,通过 caffeineCacheBuilder
方法配置了 Caffeine Cache 的一些属性,比如失效时间和最大条目数。现在,你可以在服务类中使用 @Cacheable
注解来声明缓存:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 模拟从数据库或其他数据源获取用户信息的操作
System.out.println("Fetching user info for userId: " + userId);
return "User info for userId: " + userId;
}
}
当使用 Redis 作为缓存管理器时,首先确保项目中引入了 Redis 相关的依赖。在 Maven 中,可以添加以下依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
接下来,在 Spring Boot 项目的配置文件中配置 Redis 连接信息:
# src/main/resources/application.properties
spring.redis.host=localhost
spring.redis.port=6379
然后,在 Spring Boot 项目的配置类中配置 Redis Cache 作为缓存管理器:
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(redisConnectionFactory)
.build();
}
}
在这个配置中,RedisCacheManager
被配置为缓存管理器,并使用了 RedisConnectionFactory
连接到 Redis 服务器。现在,你可以在服务类中使用 @Cacheable
注解来声明缓存:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 模拟从数据库或其他数据源获取用户信息的操作
System.out.println("Fetching user info for userId: " + userId);
return "User info for userId: " + userId;
}
}
添加以下依赖:
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>30.1-jreversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
然后,可以在 Spring Boot 项目的配置类中配置 Guava Cache 作为缓存管理器:
import com.google.common.cache.CacheBuilder;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
GuavaCacheManager cacheManager = new GuavaCacheManager();
cacheManager.setCacheBuilder(guavaCacheBuilder());
return cacheManager;
}
private CacheBuilder<Object, Object> guavaCacheBuilder() {
return CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存失效时间为10分钟
.maximumSize(100); // 设置缓存的最大条目数
}
}
@Service
public class MyService {
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 模拟从数据库或其他数据源获取用户信息的操作
System.out.println("Fetching user info for userId: " + userId);
return "User info for userId: " + userId;
}
}
在上述配置中,GuavaCacheManager
被配置为缓存管理器,通过 guavaCacheBuilder
方法配置了 Guava Cache 的一些属性,比如失效时间和最大条目数。这样,你就可以在 Spring Boot 项目中使用 Guava Cache 作为缓存容器了。在实际应用中,可以根据具体的业务需求调整 Guava Cache 的配置参数。
在 Spring 中,一个项目中可以有多个 CacheManager
,并且可以在不同的地方使用不同的 CacheManager
实例。这种情况下,每个 CacheManager
可以配置不同的缓存提供者,例如使用 Ehcache、Guava Cache 或者 Redis 等。
在实际项目中,有时候需要使用多个 CacheManager
的场景可能包括:
CacheManager
可以更好地满足各自的要求。CacheManager
分别配置本地缓存和分布式缓存。CacheManager
可以灵活配置每个缓存的过期时间。在配置多个 CacheManager
时,需要确保为它们分配唯一的名称,以便在使用时能够正确地引用。例如:
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager localCacheManager() {
// 配置本地缓存管理器
return new ConcurrentMapCacheManager("localCache");
}
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
// 配置使用 Redis 的缓存管理器
return RedisCacheManager.builder(redisConnectionFactory).build();
}
}
在上述示例中,通过 localCacheManager
和 redisCacheManager
分别配置了本地缓存管理器和 Redis 缓存管理器。可以在需要的地方使用 @Cacheable
注解时指定使用哪个缓存管理器,例如:
@Service
public class MyService {
@Cacheable(value = "localCache", key = "#userId", cacheManager = "localCacheManager")
public String getLocalUserInfo(String userId) {
// 从本地缓存获取用户信息
// ...
}
@Cacheable(value = "redisCache", key = "#userId", cacheManager = "redisCacheManager")
public String getRedisUserInfo(String userId) {
// 从 Redis 缓存获取用户信息
// ...
}
}
总的来说,虽然一个项目可以有多个 CacheManager
,但需要根据具体需求和业务场景来决定是否需要配置多个 CacheManager
,以及如何使用它们。
常见的缓存管理器有不同的实现,如 Ehcache、Caffeine、Guava Cache、Redis 等,它们之间有一些区别,选型时需要考虑项目的需求和特点。以下是一些常见缓存管理器的区别和选型考虑:
选型考虑因素:
@Cacheable
:@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 方法体逻辑
}
@CachePut
:@CachePut(value = "userCache", key = "#userId")
public String updateUserInfo(String userId, String newInfo) {
// 方法体逻辑
}
@CacheEvict
:allEntries
属性清除缓存中的所有条目,也可以通过 beforeInvocation
属性指定在方法执行前还是执行后清除缓存。 (删除缓存)@CacheEvict(value = "userCache", key = "#userId")
public void clearUserInfoCache(String userId) {
// 方法体逻辑
}
@Caching
:@Caching(
cacheable = {
@Cacheable(value = "userCache", key = "#userId"),
@Cacheable(value = "emailCache", key = "#email")
},
put = {
@CachePut(value = "userCache", key = "#userId"),
@CachePut(value = "emailCache", key = "#email")
}
)
public String getUserInfoByEmailOrId(String userId, String email) {
// 方法体逻辑
}
这些注解可以与 SpEL 表达式一起使用,以动态生成缓存键。通过在方法上使用这些注解,可以方便地配置和管理缓存,而无需手动编写缓存逻辑。要使用这些注解,你需要在配置类上添加 @EnableCaching
注解,以启用 Spring Cache 功能。
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
// 配置相关的 CacheManager 等
}
这只是 Spring Cache 的一小部分功能,你可以根据具体的需求和业务场景选择合适的注解来实现缓存。在实际使用中,还可以配置一些高级功能,比如缓存的失效策略、条件缓存、缓存的自定义过期时间等。
在 Spring Cache 中,key
属性是用于指定缓存键的关键属性,它是一个 SpEL(Spring Expression Language)表达式。这意味着你可以使用 SpEL 表达式动态地构建缓存键。以下是一些常见的 key
属性的写法:
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 方法体逻辑
}
#
符号和参数名来引用这些参数:@Cacheable(value = "userCache", key = "#userId + '-' + #userName")
public String getUserInfo(String userId, String userName) {
// 方法体逻辑
}
@Cacheable(value = "userCache", key = "#userId.concat('-').concat(#userName.toUpperCase())")
public String getUserInfo(String userId, String userName) {
// 方法体逻辑
}
result
关键字引用方法的返回值:@Cacheable(value = "userCache", key = "#result.userId")
public UserInfo getUserInfo(String userId) {
// 方法体逻辑
}
@Cacheable(value = "userCache", key = "#myUtil.generateKey(#userId)")
public String getUserInfo(String userId) {
// 方法体逻辑
}
在这个例子中,假设你已经在项目中定义了 myUtil
这个工具类,并在该类中有一个 generateKey
方法。
@Cacheable
、@CachePut
、@CacheEvict
注解时,可以通过 condition
属性设置缓存失效的条件。例如,只有满足某个条件时才进行缓存:@Cacheable(value = "userCache", key = "#userId", condition = "#result != null && #result.isValid()")
public UserInfo getUserInfo(String userId) {
// 方法体逻辑
}
unless
属性设置条件,如果满足条件,则不会缓存结果。与 condition
相反,unless
表达式的值为 true
时,不缓存:@Cacheable(value = "userCache", key = "#userId", unless = "#result.isDisabled()")
public UserInfo getUserInfo(String userId) {
// 方法体逻辑
}
ConcurrentMapCacheManager
之外的缓存管理器,可以自定义 CacheManager
:@Configuration
@EnableCaching
public class CustomCacheConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager() {
// 自定义的缓存管理器
return new MyCustomCacheManager();
}
}
@MyCustomCacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
// 方法体逻辑
}
其中 @MyCustomCacheable
是一个自定义的缓存注解,你需要在配置类中注册它,并实现相应的缓存逻辑。
@Cacheable
的 expire
属性:@Cacheable(value = "userCache", key = "#userId", expire = 600) // 过期时间为 600 秒
public UserInfo getUserInfo(String userId) {
// 方法体逻辑
}