1.Redis 缓存实现
1.1 编程式缓存(Spring Data Redis)
编程式缓存使用 Spring Data Redis 框架,通过 RedisTemplate 操作 Redis,结合 Redisson 客户端实现。本例以缓存 OAuth2 访问令牌(OAuth2AccessTokenDO)为例。
步骤 1:添加依赖
项目引入了 Redisson 的 Spring Boot Starter 依赖,以支持 Redis 操作。
java
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
- 解释:Redisson 是一个功能强大的 Redis 客户端,支持分布式锁、队列、限流等功能。引入此依赖后,Spring Boot 可以通过 Redisson 连接 Redis,并使用 RedisTemplate 进行编程式缓存操作。
步骤 2:配置 Redis 连接
在 application-local.yaml 文件中,通过 spring.redis 配置项设置 Redis 连接参数。
java
redis:
host: 127.0.0.1 # Redis 服务器地址
port: 6379 # Redis 端口
database: 0 # Redis 数据库索引
# password: dev # 密码(生产环境建议启用)
- 解释:此配置指定了 Redis 服务器的地址、端口和数据库索引。yudao 项目指出 Redisson 的默认配置通常无需额外调优,适用于大多数场景。
步骤 3:配置 RedisTemplate(JSON 序列化)
在 YudaoRedisAutoConfiguration 类中,项目自定义了 RedisTemplate,使用 JSON 序列化存储复杂对象的值。
java
@AutoConfiguration(before = RedissonAutoConfiguration.class)
public class YudaoRedisAutoConfiguration {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 创建 RedisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置键序列化(String)
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置值序列化(JSON)
template.setValueSerializer(buildRedisSerializer());
template.setHashValueSerializer(buildRedisSerializer());
return template;
}
public static RedisSerializer<?> buildRedisSerializer() {
RedisSerializer<Object> json = RedisSerializer.json();
// 支持 LocalDateTime 序列化
ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper");
objectMapper.registerModules(new JavaTimeModule());
return json;
}
}
解释:
- 目的:定义一个 RedisTemplate bean,用于 Redis 的键值操作。
- 序列化 :
- 键:使用 StringRedisSerializer,确保键以字符串形式存储(如 oauth2_access_token:token123)。
- 值:使用 Jackson2JsonRedisSerializer(通过 RedisSerializer.json()),将复杂对象(如 OAuth2AccessTokenDO)序列化为 JSON。
- LocalDateTime 支持:通过注册 JavaTimeModule,确保 Jackson 可以正确序列化/反序列化 Java 8 的日期时间类型(如 LocalDateTime)。
- 优先级:@AutoConfiguration(before = RedissonAutoConfiguration.class) 确保自定义 RedisTemplate 在 Redisson 默认配置之前加载,以覆盖默认设置。
步骤 4:定义数据对象(OAuth2AccessTokenDO)
项目定义了一个数据对象 OAuth2AccessTokenDO,用于表示存储在 Redis 中的访问令牌结构。
java
@TableName(value = "system_oauth2_access_token", autoResultMap = true)
@KeySequence("system_oauth2_access_token_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class OAuth2AccessTokenDO extends TenantBaseDO {
@TableId
private Long id; // 主键
private String accessToken; // 访问令牌
private String refreshToken; // 刷新令牌
private Long userId; // 用户编号
private Integer userType; // 用户类型
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, String> userInfo; // 用户信息
private String clientId; // 客户端编号
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> scopes; // 授权范围
private LocalDateTime expiresTime; // 过期时间
}
解释:
- 作用:OAuth2AccessTokenDO 是一个数据对象(DO),用于映射数据库表 system_oauth2_access_token 和 Redis 中的缓存数据。
- 注解 :
- @TableName:指定 MyBatis-Plus 映射的表名。
- @KeySequence:支持 Oracle、PostgreSQL 等数据库的主键自增(MySQL 可忽略)。
- @TableField(typeHandler = JacksonTypeHandler.class):对复杂字段(如 userInfo 和 scopes)使用 Jackson 序列化,存储为 JSON。
- 字段:包含访问令牌、用户编号、过期时间等信息,适合缓存复杂对象。
步骤 5:定义 Redis Key 常量(RedisKeyConstants)
项目通过 RedisKeyConstants 类集中管理 Redis 键,避免键名散落在代码中。
java
public interface RedisKeyConstants {
String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
// 其他键定义...
}
解释:
- 目的:统一管理 Redis 键的格式,类似数据库表的规范管理。
- 键格式:OAUTH2_ACCESS_TOKEN 使用 %s 占位符,动态生成键(如 oauth2_access_token:token123)。
- 优势:通过查看 RedisKeyConstants,可以快速了解模块使用的所有 Redis 键,便于维护和调试。
步骤 6:实现 Redis 数据访问对象(OAuth2AccessTokenRedisDAO)
OAuth2AccessTokenRedisDAO 类封装了对 OAuth2AccessTokenDO 的 Redis 缓存操作。
java
@Repository
public class OAuth2AccessTokenRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
public OAuth2AccessTokenDO get(String accessToken) {
String redisKey = formatKey(accessToken);
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
}
public void set(OAuth2AccessTokenDO accessTokenDO) {
String redisKey = formatKey(accessTokenDO.getAccessToken());
// 清理多余字段,避免缓存
accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null);
long time = LocalDateTimeUtil.between(LocalDateTime.now(), accessTokenDO.getExpiresTime(), ChronoUnit.SECONDS);
if (time > 0) {
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO), time, TimeUnit.SECONDS);
}
}
public void delete(String accessToken) {
String redisKey = formatKey(accessToken);
stringRedisTemplate.delete(redisKey);
}
public void deleteList(Collection<String> accessTokens) {
List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
stringRedisTemplate.delete(redisKeys);
}
private static String formatKey(String accessToken) {
return String.format(OAUTH2_ACCESS_TOKEN, accessToken);
}
}
解释:
- 注入:@Resource private StringRedisTemplate stringRedisTemplate 注入 StringRedisTemplate,用于操作字符串类型的键值对(值仍为 JSON)。
- 方法 :
- get:从 Redis 获取指定访问令牌的 OAuth2AccessTokenDO,通过 JsonUtils.parseObject 反序列化 JSON 为对象。
- set:将 OAuth2AccessTokenDO 序列化为 JSON 存储到 Redis,设置动态过期时间(基于 expiresTime)。清理多余字段(如 updater)以减少缓存体积。
- delete 和 deleteList:删除单个或多个访问令牌的缓存。
- 键生成:formatKey 使用 RedisKeyConstants.OAUTH2_ACCESS_TOKEN 格式化键名,确保一致性。
步骤 7:业务逻辑中使用 Redis 缓存
在 OAuth2TokenServiceImpl 中,通过注入 OAuth2AccessTokenRedisDAO,实现访问令牌的缓存操作。
java
@Service
public class OAuth2TokenServiceImpl {
@Resource
private OAuth2AccessTokenRedisDAO accessTokenRedisDAO;
// 示例方法:获取访问令牌
public OAuth2AccessTokenDO getAccessToken(String accessToken) {
// 先从 Redis 获取
OAuth2AccessTokenDO tokenDO = accessTokenRedisDAO.get(accessToken);
if (tokenDO != null) {
return tokenDO;
}
// 如果 Redis 没有,从数据库查询
tokenDO = accessTokenMapper.selectByAccessToken(accessToken);
if (tokenDO != null) {
// 存入 Redis
accessTokenRedisDAO.set(tokenDO);
}
return tokenDO;
}
}
解释:
- 缓存优先:先尝试从 Redis 获取令牌,若存在则直接返回。
- 数据库回查:若 Redis 缓存不存在,则从数据库查询,并将结果存入 Redis。
- 封装性:OAuth2AccessTokenRedisDAO 屏蔽了底层的 Redis 操作,业务代码只需调用 DAO 方法,逻辑简洁。
1.2 声明式缓存(Spring Cache)
声明式缓存基于 Spring Cache 框架,使用注解(如 @Cacheable)简化缓存操作。本例以角色(RoleDO)缓存为例。
步骤 1:添加依赖
引入 Spring Cache 依赖以启用注解式缓存。
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 解释:此依赖启用 Spring Cache 功能,支持 @Cacheable、@CachePut 和 @CacheEvict 注解。
步骤 2:配置 Redis 作为缓存后端
在 application.yaml 中配置 Redis 连接(与编程式缓存相同),并在 YudaoCacheAutoConfiguration 类中配置 Spring Cache。
java
@AutoConfiguration
@EnableConfigurationProperties({CacheProperties.class, YudaoCacheProperties.class})
@EnableCaching
public class YudaoCacheAutoConfiguration {
@Bean
@Primary
public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 使用单冒号作为键前缀分隔符
config = config.computePrefixWith(cacheName -> {
String keyPrefix = cacheProperties.getRedis().getKeyPrefix();
if (StringUtils.hasText(keyPrefix)) {
keyPrefix = keyPrefix.endsWith(":") ? keyPrefix : keyPrefix + ":";
return keyPrefix + cacheName + ":";
}
return cacheName + ":";
});
// 使用 JSON 序列化
config = config.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(YudaoRedisAutoConfiguration.buildRedisSerializer()));
// 设置缓存属性
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
RedisCacheConfiguration redisCacheConfiguration,
YudaoCacheProperties yudaoCacheProperties) {
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
}
}
解释:
- 启用缓存:@EnableCaching 激活 Spring Cache 功能。
- 键前缀:自定义键前缀格式(如 cacheName:),使用单冒号分隔,避免工具(如 Redis Desktop Manager)显示问题。
- 序列化:复用 YudaoRedisAutoConfiguration.buildRedisSerializer(),确保值以 JSON 格式存储。
- 缓存属性 :
- timeToLive:设置默认缓存过期时间(项目默认 1 小时)。
- disableCachingNullValues:禁止缓存 null 值。
- disableKeyPrefix:可选禁用键前缀。
- RedisCacheManager:创建缓存管理器,基于 Redis 实现缓存存储。
步骤 3:使用 Spring Cache 注解
在 RoleServiceImpl 中,使用 @Cacheable 和 @CacheEvict 实现角色缓存。
java
@Service
public class RoleServiceImpl {
@Resource
private RoleMapper roleMapper;
@Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null")
public RoleDO getRoleFromCache(Long id) {
return roleMapper.selectById(id);
}
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
public void deleteRole(Long id) {
RoleDO role = validateRoleForUpdate(id);
roleMapper.deleteById(id);
permissionService.processRoleDeleted(id);
LogRecordContext.putVariable("role", role);
}
}
- 解释 :
- @Cacheable :
- value = RedisKeyConstants.ROLE:指定缓存名称(对应 Redis 键前缀,如 role:)。
- key = "#id":使用方法参数 id 作为缓存键(如 role:123)。
- unless = "#result == null":避免缓存 null 值。
- 执行逻辑:先检查 Redis 是否有缓存,若有则返回;否则执行方法并缓存结果。
- @CacheEvict:在删除角色时,移除对应的缓存键(如 role:123),确保缓存与数据库一致。
- 被动读策略:仅在查询时缓存数据(getRoleFromCache),更新或删除时清除缓存(deleteRole),避免缓存非必要数据。
- @Cacheable :
步骤 4:缓存与数据库一致性
yudao 项目采用被动读策略:
- 查询:从 Redis 获取数据,若无则从 MySQL 查询并缓存。
- 更新/删除:更新 MySQL 后,删除 Redis 缓存,确保下次查询时重新从数据库加载。
- 原因 :
- 保证 Redis 和 MySQL 数据一致性。
- 避免主动写入非必要数据,节省 Redis 存储空间。
2.在新项目中实现 Redis 缓存
2.1 项目结构
XML
new-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ │ ├── config/
│ │ │ │ └── RedisConfig.java
│ │ │ ├── dao/
│ │ │ │ └── UserRedisDAO.java
│ │ │ ├── entity/
│ │ │ │ └── UserDO.java
│ │ │ ├── service/
│ │ │ │ ├── UserService.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ └── constants/
│ │ │ └── RedisKeyConstants.java
│ │ └── resources/
│ │ └── application.yml
│ └── pom.xml
2.2 依赖配置
在 pom.xml 中添加必要的依赖。
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis 依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.25.0</version>
</dependency>
<!-- Spring Cache 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!-- MySQL 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Hutool 工具库 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
</dependency>
</dependencies>
</project>
解释:
- spring-boot-starter-web:提供 Web 功能。
- redisson-spring-boot-starter:支持 Redis 操作。
- spring-boot-starter-cache:支持 Spring Cache 注解。
- mybatis-plus-boot-starter:用于数据库操作。
- mysql-connector-java:MySQL 数据库驱动。
- lombok:简化代码。
- hutool-all:提供 JSON 序列化等工具,模拟 yudao 的 JsonUtils。
2.3 配置 Redis 连接
在 application.yml 中配置 Redis 和数据库连接。
java
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
cache:
redis:
time-to-live: 3600000 # 缓存默认过期时间 1 小时(毫秒)
key-prefix: "cache:" # 缓存键前缀
cache-null-values: false # 不缓存 null 值
mybatis-plus:
mapper-locations: classpath*:/mapper/*.xml
解释:
- Redis 配置:与 yudao 一致,指定本地 Redis 服务器。
- Cache 配置:设置默认过期时间为 1 小时,使用 cache: 前缀。
- MySQL 配置:连接本地 MySQL 数据库(需创建 demo 数据库)。
- MyBatis-Plus:配置 Mapper XML 文件路径。
2.4 配置 RedisTemplate 和 Spring Cache
创建 RedisConfig 类,配置 RedisTemplate 和 Spring Cache。
java
package com.example.demo.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@AutoConfiguration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jsonSerializer());
template.setHashValueSerializer(jsonSerializer());
return template;
}
private RedisSerializer<Object> jsonSerializer() {
RedisSerializer<Object> json = RedisSerializer.json();
ObjectMapper mapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper");
mapper.registerModule(new JavaTimeModule());
return json;
}
@Bean
@Primary
public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.computePrefixWith(cacheName -> cacheProperties.getRedis().getKeyPrefix() + cacheName + ":");
config = config.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
return config;
}
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
RedisCacheConfiguration redisCacheConfiguration) {
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
return new RedisCacheManager(cacheWriter, redisCacheConfiguration);
}
}
- 解释 :
- RedisTemplate:与 yudao 一致,使用字符串序列化键,JSON 序列化值,支持 LocalDateTime。
- Spring Cache:启用 @EnableCaching,配置 RedisCacheConfiguration 使用 JSON 序列化和自定义键前缀。
- RedisCacheManager:创建缓存管理器,基于 Redis 存储缓存。
注意:ReflectUtil 模拟 yudao 的反射操作,需替换为 Hutool 的 ReflectUtil 或直接修改 ObjectMapper:
import cn.hutool.core.util.ReflectUtil;
2.5 定义数据对象(UserDO)
创建 UserDO 类,表示用户信息。
java
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@TableName("user")
@Data
public class UserDO {
@TableId
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
解释:UserDO 映射数据库表 user,包含基本用户信息。需在 MySQL 中创建表:
java
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
create_time DATETIME
);
2.6 定义 Redis 键常量
创建 RedisKeyConstants 类,定义 Redis 键。
java
package com.example.demo.constants;
public interface RedisKeyConstants {
String USER = "user:%s";
}
解释:定义用户缓存的键格式,如 user:123。
2.7 实现 Redis 数据访问对象(UserRedisDAO)
创建 UserRedisDAO 类,封装用户缓存操作。
java
package com.example.demo.dao;
import cn.hutool.json.JSONUtil;
import com.example.demo.constants.RedisKeyConstants;
import com.example.demo.entity.UserDO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Repository
public class UserRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
public UserDO get(Long id) {
String redisKey = formatKey(id);
String value = stringRedisTemplate.opsForValue().get(redisKey);
return JSONUtil.toBean(value, UserDO.class);
}
public void set(UserDO userDO) {
String redisKey = formatKey(userDO.getId());
userDO.setCreateTime(null); // 清理多余字段
stringRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(userDO), 3600, TimeUnit.SECONDS);
}
public void delete(Long id) {
String redisKey = formatKey(id);
stringRedisTemplate.delete(redisKey);
}
private static String formatKey(Long id) {
return String.format(RedisKeyConstants.USER, id);
}
}
解释:
- 注入:使用 StringRedisTemplate 操作 JSON 字符串。
- 方法 :
- get:从 Redis 获取用户数据,反序列化为 UserDO。
- set:将 UserDO 序列化为 JSON,设置 1 小时过期时间。
- delete:删除指定用户的缓存。
- JSON 处理:使用 Hutool 的 JSONUtil 替代 yudao 的 JsonUtils。
2.8 实现业务逻辑
创建 UserService 接口和 UserServiceImpl 实现类,支持编程式和声明式缓存。
java
package com.example.demo.service;
import com.example.demo.entity.UserDO;
public interface UserService {
UserDO getUser(Long id); // 编程式缓存
UserDO getUserFromCache(Long id); // 声明式缓存
void deleteUser(Long id);
}
java
package com.example.demo.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.constants.RedisKeyConstants;
import com.example.demo.dao.UserRedisDAO;
import com.example.demo.entity.UserDO;
import com.example.demo.mapper.UserMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private UserRedisDAO userRedisDAO;
@Override
public UserDO getUser(Long id) {
UserDO user = userRedisDAO.get(id);
if (user != null) {
return user;
}
user = userMapper.selectById(id);
if (user != null) {
userRedisDAO.set(user);
}
return user;
}
@Override
@Cacheable(value = RedisKeyConstants.USER, key = "#id", unless = "#result == null")
public UserDO getUserFromCache(Long id) {
return userMapper.selectById(id);
}
@Override
@CacheEvict(value = RedisKeyConstants.USER, key = "#id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
// 缓存通过 @CacheEvict 自动清除
}
}
- 解释 :
- 编程式缓存(getUser):先查 Redis,若无则查数据库并缓存。
- 声明式缓存(getUserFromCache):使用 @Cacheable,自动处理缓存逻辑。
- 删除(deleteUser):删除数据库记录并通过 @CacheEvict 清除缓存。