
Spring Boot 提供了对缓存的简便支持,使得开发者能够通过简单的注解实现缓存操作,减少重复代码的编写。本文将详细介绍如何在 Spring Boot 3 中使用 Redis 作为缓存,并通过注解驱动的方式进行缓存操作。
Redis 是一个高效的键值对存储系统,特别适合于构建高性能、可扩展的缓存层。其优点包括:
Spring Boot 提供了对缓存的开箱即用支持,开发者只需简单配置即可使用。具体参考上一章 重学SpringBoot3-集成Redis(一)。
在 pom.xml 中引入 Redis 和 Spring Cache 相关依赖:
<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>在 application.yml 中,配置 Redis 服务器地址及相关连接池配置:
spring:
cache:
type: redis # 使用 Redis 作为缓存类型
data:
redis:
host: localhost
port: 6379 # Redis 端口
password: # 如果有密码可以在这里配置
lettuce:
pool:
max-active: 100 # 最大并发连接数
max-idle: 50 # 最大空闲连接数
min-idle: 10 # 最小空闲连接数在 Spring Boot 项目中,使用 @Cacheable 注解前,需要通过 @EnableCaching 注解启用缓存功能。可以在主应用类或者任何配置类中加上这个注解:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}Spring 提供了一组注解用于操作缓存,这些注解可以直接应用于方法上,使得代码更简洁。常用注解包括:
@Cacheable:用于标记一个方法的返回值是可缓存的。下一次调用该方法时,Spring 会直接从缓存中返回结果,而不是再次执行方法。@CachePut:在方法执行后将返回值放入缓存。它与 @Cacheable 的区别在于,@CachePut 不会跳过方法执行,而是始终执行方法并更新缓存。@CacheEvict:用于清除缓存中的某些条目,可以指定缓存的 key 或清空整个缓存空间。@Cacheable 示例import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 当方法第一次调用时,结果将被缓存起来,之后相同参数的调用将直接从缓存中获取数据
@Cacheable(value = "user", key = "#p0")
public User getUserById(Long id) {
// 模拟数据库查询操作
System.out.println("Fetching user with id: " + id);
return new User(id, "User" + id);
}
}解释:
@Cacheable 用于缓存方法的返回值。value = "user" 指定了缓存的名称,即 "user"。key = "#p0" 指定了缓存的键值。这里的 #p0 是一个 SpEL 表达式,表示方法的第一个参数。在这个例子中,方法 getUserById 第一次被调用时,结果会缓存到 Redis 中,并与 user::id 作为 key 存储。后续相同 id 的请求将直接从缓存返回,而无需执行方法。

@CachePut 示例有时候,我们希望方法执行后,不仅返回结果,还更新缓存,这时可以使用 @CachePut 注解。
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 无论缓存中是否存在数据,该方法都会被执行,并且返回值会更新缓存
@CachePut(value = "user", key = "#p0.id")
public User updateUser(User user) {
System.out.println("Updating user with id: " + user.getId());
// 模拟数据库更新操作
user.setName("Updated " + user.getName());
return user;
}
}解释:
@CachePut 用于更新缓存中的值。value = "user" 指定了缓存的名称,即 "user"。key = "#p0.id" 指定了缓存的键值。这里的 #p0 是一个 SpEL 表达式,表示方法的第一个参数,即 User 对象。.id 表示取 User 对象的 id 属性作为缓存键。连续两次调用 curl "http://localhost:8080/api/redis/updAndSave?id=2" ,可以从日志中看到,每次方法都执行了,并且 user 对象加入到了缓存中。

@CacheEvict 示例为了保持缓存数据的准确性,某些情况下需要手动清除缓存中的数据。@CacheEvict 注解允许我们在数据修改或删除时,移除缓存中的旧数据。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 清除缓存中的指定用户数据
@CacheEvict(value = "user", key = "#p0")
public void deleteUser(Long id) {
System.out.println("Deleting user with id: " + id);
// 模拟数据库删除操作
}
// 清空整个缓存空间
@CacheEvict(value = "user", allEntries = true)
public void clearCache() {
System.out.println("Clearing all user cache");
}
}调用 curl "http://localhost:8080/api/redis/delUser?id=1" ,删除 key 为 user::1 的缓存。

调用 curl "http://localhost:8080/api/redis/delAllUser" ,删除所有前缀为 user:: 的缓存。

以上缓存名称、过期时间和序列化方式都是默认设置,Spring 允许我们自定义缓存管理器。在大多数情况下,默认配置足够使用,但如果需要定制化的缓存行为,我们可以自定义缓存配置。通过实现 RedisCacheConfiguration,我们可以设置缓存的过期时间、序列化方式等。
Spring Boot 3 中,RedisCacheConfiguration 类是用于配置 Redis 缓存行为的核心组件之一。它提供了多种方法,用于自定义 Redis 缓存的各类设置,比如缓存过期时间、序列化策略等。以下是一些常用的配置选项:
设置缓存条目的默认生存时间(TTL)。这决定了缓存数据在 Redis 中保留的时间。
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)); // 设置缓存10分钟后过期默认情况下,Redis 使用二进制存储键和值。RedisCacheConfiguration 提供了设置键(key)序列化方式的方法。常见的序列化方式包括 StringRedisSerializer,可以使用它来确保键以字符串格式存储:
import org.springframework.data.redis.serializer.StringRedisSerializer;
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));同样,值(value)的序列化方式也可以自定义。常用的序列化方式有 GenericJackson2JsonRedisSerializer,它将对象序列化为 JSON 格式:
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));你可以配置不缓存空值,避免 Redis 存储 null,减少缓存的无效占用。
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues();Redis 中默认会为缓存键值加上一个命名空间的前缀,以防止不同缓存键冲突。可以自定义这个前缀,也可以关闭它:
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.prefixCacheNameWith("myApp::") // 自定义缓存键前缀
.computePrefixWith(cacheName -> "customPrefix::" + cacheName + "::"); // 使用自定义逻辑要禁用前缀:
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.disableKeyPrefix();你可以设置一个键的空闲时间,Redis 将会在指定的时间内删除不再被访问的键。
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.enableTimeToIdle();可以将多个配置组合到一起:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
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
public class CacheConfig {
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 缓存的过期时间
.disableCachingNullValues() // 不缓存 null 值
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // 自定义 key 序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // 自定义 value 序列化
}
}可以为不同的缓存区域设置不同的过期策略。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5));
RedisCacheConfiguration longLivedConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put("shortLivedCache", defaultConfig);
cacheConfigurations.put("longLivedCache", longLivedConfig);
return RedisCacheManager.builder(connectionFactory)
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}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.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class CacheConfig {
@Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 设置缓存过期时间为 10 分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // 自定义 Key 序列化器
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); // 自定义 Value 序列化器
}
}通过这种方式,我们可以对缓存的过期时间、序列化方式进行更细粒度的控制。

尽管 Redis 是一个高效的缓存解决方案,但在实际应用中,仍然有一些需要注意的问题:
通过本文,我们学习了如何在 Spring Boot 3 和 Java 17 中使用 Redis 作为缓存。Spring 提供了注解驱动的缓存操作方式,使得缓存操作变得非常简单易用。通过合理配置和使用缓存,我们能够极大地提升系统的性能和响应速度。在生产环境中,合理的缓存策略、过期时间和缓存层优化将会进一步提高系统的稳定性和扩展能力。