缓存框架-Spring Cache
概述
Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:
- EHCache
- Caffeine
- Redis(常用)
环境准备
导入Redis和SpringCache依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
添加配置支持
在application.yml文件中添加redis的支持
spring: redis: host: 127.0.0.1 port: 6379 password: 123456
常用注解
在SpringCache中提供了很多缓存操作的注解,常见的是以下几个:
<!--br {mso-data-placement:same-cell;}--> td {white-space:nowrap;border:1px solid #dee0e3;font-size:10pt;font-style:normal;font-weight:normal;vertical-align:middle;word-break:normal;word-wrap:normal;}
注解 | 说明 |
@EnableCaching | 开启缓存注解功能,通常加在启动类上 |
@Cacheable | 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 |
@CachePut | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
@Caching | 缓存的结合体,可以组合以上注解在一个方法中使用,比如有新增,有删除 |
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。
- @Cacheable
在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中,查询的时候使用
@Cacheable(cacheNames = "userCache",key="#id") public User getById(Long id){ User user = userMapper.getById(id); if(user == null){ throw new RuntimeException("用户不存在"); } return user; }
存储到redis之后的数据是这样的
- cacheNames = “userCache” : 表示命名空间,key的第一层级
- key=“#id” : id指的是查询的参数, 也就是使用id属性作为key
- 在Redis中,冒号通常用作键的命名约定,可以创建层次结构,类似于文件系统中的路径结构,提升查找效率
注意:根据演示,发现对应的value是会出现乱码,可读性不好,那么怎么办呢?
解决办法:【可选】
@Configuration @Slf4j public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ log.info("开始创建redis模板对象..."); RedisTemplate redisTemplate = new RedisTemplate(); //设置redis的连接工厂对象 redisTemplate.setConnectionFactory(redisConnectionFactory); //设置redis key的序列化器 redisTemplate.setKeySerializer(new StringRedisSerializer()); return redisTemplate; } }
如果要利用RedisTemplate去实现缓存操作的话,代码如下:
service层代码
public User getById(Long id){ //1.查询redis String userStr = redisTemplate.opsForValue().get("redis:" + id); if (userStr!=null){ //命中 User user = JSONObject.parseObject(userStr, User.class); return user; } //未命中 User user = userMapper.getById(id); if(user == null){ throw new RuntimeException("用户不存在"); } //同步数据到redis中 redisTemplate.opsForValue().set("redis:" + id,JSON.toJSONString(user)); return user; }
其中在redis中存储的数据是经过序列化之后的数据?那什么是序列化呢?
- 对象序列化是将对象转换为可存储或传输的字节序列的过程,方便在需要时可以重新创建对象。这种序列化后的字节序列可以保存在文件,数据库或通过网络进行传输。
多条件查询使用Cacheable,比如查询的方法参数有多个,我们可以使用hashcode作为缓存的key,如下代码
@Cacheable(value = "userCache",key="#userDto.hashCode()",unless = "#result.size() == 0") public List<User> getList(UserDto userDto){ List<User> list = userMapper.getList("%" + userDto.getName() + "%", userDto.getAge()); return list; }
在UserDto 中需要提供一个hashCode方法
@Data public class UserDto { private String name; private int age; @Override public int hashCode() { int result = Objects.hash(getName(),getAge()); return result; } }
hashcode的特点是,只要参数相同,则生成后的hashcode值肯定相同
如果返回结果为空,则不缓存unless="#result == null"或unless="#result.size()==0"
@CachePut
作用: 将方法返回值,放入缓存,一般保存的时候使用该注解
@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1 public User insert(User user){ userMapper.insert(user); return user; }
- #user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key
@CacheEvict
作用: 清理指定缓存
@CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据 public void deleteById(Long id){ userMapper.deleteById(id); } @CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据 public void deleteAll(){ userMapper.deleteAll(); }
@Caching[慎用]
作用: 组装其他缓存注解
- cacheable 组装一个或多个@Cacheable注解
- put 组装一个或多个@CachePut注解
- evict 组装一个或多个@CacheEvict注解
@Caching( cacheable = { @Cacheable(value = "userCache",key = "#id") }, put = { @CachePut(value = "userCache",key = "#result.name"), @CachePut(value = "userCache",key = "#result.age") } ) public User insert(User user){ userMapper.insert(user); return user; }