1、Spring Cache
是 Spring 框架提供的缓存抽象层,提供统一的缓存接口和注解方式,通过一组简单的注解,能够轻松为应用程序添加缓存功能,而无需关注底层缓存的具体实现。
- 支持多种缓存实现(Redis,EhCache等)
- 通过注解就能控制缓存行为
- 不依赖某一种具体缓存,可以切换缓存而不更改业务逻辑
1.1 缓存注解
- @Cacheable:缓存查询,用于标记一个方法,表示其返回值可以缓存。
//标记一个方法的返回值可以被缓存
//缓存的键和值可以自定义
@Cacheable(value = "users", key = "#userId")
public User getUserById(String userId) {
// 这里是查询用户的业务逻辑
return userRepository.findById(userId);
}
- @CachePut:更新缓存,用于方法执行后更新缓存(即使缓存已有数据,也会执行方法并更新缓存)。
//该注解标记的方法总是执行,并且其返回值会更新到缓存中
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 更新用户的逻辑
return userRepository.save(user);
}
- @CacheEvict:用于清除缓存。
//清除缓存,可以是指定缓存的某个值,也可以清除整个缓存
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
// 删除用户的业务逻辑
userRepository.deleteById(userId);
}
- @Caching:组合注解,复杂缓存策略可组合使用
1.2 缓存存储和策略
- Spring 提供了 CacheManager 接口,支持多种不同的缓存实现,可以通过配置指定缓存的存储方式。
2、配置Spring Cache + Redis
2.1 添加依赖 pom.xml
<!-- Spring Cache 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 配置Redis连接 application.yml
spring:
cache:
type: redis # 使用Redis 作为缓存
#Redis配置
redis:
host: localhost
port: 6379
password: #没有密码留空
2.3 开启缓存支持
使用 @EnableCaching 注解启用缓存,
在启动类或者配置类上添加注解。
2.4 配置缓存管理器:Redis CacheManager
Spring Boot 默认会自动配置 RedisCacheManager,但如果想自定义缓存过期时间、序列化方式,可以自己配置。
package com.example.config;
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.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 CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) //默认过期时间
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //指定缓存Key的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues(); //禁止缓存null值
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}
2.5 在 Service 层使用缓存注解
使用@Cacheable、@CacheEvict
改造DepartmentService,替换原本使用的 Redis缓存(Redis集成与基础操作1、Redis是什么 高性能内存数据库 常用于:缓存(Cache),Session存储,分布式锁 - 掘金)
@Service
public class DepartmentServiceImpl implements DepartmentService {
@Autowired
private DepartmentMapper departmentMapper;
// 使用Spring Cache注解
@Cacheable(value = "departments", key = "'all'")
@Override
public List<Department> findAll() {
return departmentMapper.selectList(null);
}
@Cacheable(value = "departments", key = "#id")
@Override
public Department findById(Integer id) {
return departmentMapper.selectById(id);
}
@CacheEvict(value = "departments", allEntries = true) // 清空所有科室缓存
@Override
public boolean updateDepartment(Department department) {
int rows = departmentMapper.updateById(department);
return rows > 0;
}
}
分页查询缓存:
@Service
public class AppointmentServiceImpl implements AppointmentService {
// 分页查询缓存(注意:分页参数影响key)
@Cacheable(value = "appointments", key = "'user:' + #userId + '_page:' + #pageNum + '_size:' + #pageSize")
@Override
public IPage<Appointment> findAppointmentsByUserId(Integer userId, Integer pageNum, Integer pageSize) {
Page<Appointment> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Appointment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Appointment::getUserId, userId)
.orderByDesc(Appointment::getAppointmentDate);
return appointmentMapper.selectPage(page, wrapper);
}
// 预约操作后清除该用户的预约缓存
@CacheEvict(value = "appointments", key = "'user:' + #userId + '_page:*'", allEntries = true)
@Override
@Transactional(rollbackFor = Exception.class)
public boolean bookAppointment(Integer userId, Integer deptId, String appointmentDate) {
// ... 原业务逻辑不变
}
}