通用缓存SpringCache
1.实现缓存逻辑有2种方式:
2.每个接口单独控制缓存逻辑
统一控制缓存逻辑Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;
每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
内部使用AOP的形式,对redis操作进行简化
重要概念
名称 | 解释 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其进行缓存 |
@CacheEvict | 清空缓存 |
入门案例
导入依赖
导入SpringDataRedis的依赖,并在application.yml中配置 (略)
开启缓存支持
然后在启动类注解@EnableCaching开启缓存
@SpringBootApplication @EnableCaching //开启缓存 public class DemoApplication{ public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
编写UserInfoService
package com.tanhua.server.test; import com.tanhua.domain.db.UserInfo; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class UserInfoService { //根据id查询 public UserInfo queryById(Long userId) { //从数据库查询 UserInfo user = new UserInfo(); user.setId(userId); user.setNickname("ceshi"); return user; } //根据id修改 public void update(Long userId) { UserInfo user = new UserInfo(); user.setId(userId); user.setNickname("itcast"); } }
缓存@Cacheable
@Cacheable
注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存
@Cacheable(value = "user",key = "#userId") public UserInfo queryById(Long userId) { //从数据库查询 UserInfo user = new UserInfo(); user.setId(userId); user.setNickname("ceshi"); return user; }
此处的value是必需的,它指定了你的缓存存放在哪块命名空间。
此处的key是使用的spEL表达式,参考上章。这里有一个小坑,如果你把methodName换成method运行会报错,观察它们的返回类型,原因在于methodName是String而methoh是Method。
此处的User实体类一定要实现序列化public class User implements Serializable,否则会报java.io.NotSerializableException异常。
到这里,你已经可以运行程序检验缓存功能是否实现。
清除@CacheEvict
@CachEvict
的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空 。
//根据id修改 @CacheEvict(value = "user",key = "#userId") public void update(Long userId) { //修改用户 UserInfo user = new UserInfo(); user.setId(userId); user.setNickname("itcast"); }
视频列表缓存处理
修改VideoService,分页列表存入缓存,发布视频删除缓存
由于使用Reids缓存处理数据时,不能缓存ResponseEntity对象,所以需要修改方法返回值为
PageResult
@Cacheable(value="videoList",key="#page + '_' + #pagesize") public PageResult queryVideoList(Integer page, Integer pagesize) { //1、调用API查询 : PageReulst<Video> PageResult result = videoApi.findAll(page,pagesize); //2、获取分页中的list集合 List<Video> List<Video> items = (List<Video>)result.getItems(); //3、循环视频列表,一个Video构造一个Vo List<VideoVo> list = new ArrayList<>(); for (Video item : items) { UserInfo userInfo = userInfoApi.findById(item.getUserId()); VideoVo vo = VideoVo.init(userInfo, item); //从redis中获取,当前用户是否已经关注了视频发布作者 String key = "followUser_"+UserHolder.getUserId()+"_"+item.getUserId(); if (redisTemplate.hasKey(key)) { vo.setHasFocus(1); } list.add(vo); } //4、替换result中的item数据 result.setItems(list); //5、构造返回值 result; }
发布视频清空缓存
修改VideoService,分页列表存入缓存,发布视频删除缓存
由于使用Reids缓存处理数据时,不能缓存ResponseEntity对象,所以需要修改方法返回值为
PageResult
//发布视频 @CacheEvict(value="videoList",allEntries = true) public ResponseEntity saveVideo(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException { //1、图片上传到阿里云oss,获取请求地址 String picUrl = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream()); //2、视频上传到fdfs上,获取请求地址 String filename = videoFile.getOriginalFilename(); //xxxx.avi //获取文件后缀 String sufix = filename.substring(filename.lastIndexOf(".")+1); StorePath storePath = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), sufix, null); //文件输入流,文件长度(大小),文件的后缀名,元数据(null) String videoUrl = webServer.getWebServerUrl() + storePath.getFullPath(); //3、构建Video对象,并设置属性 Video video = new Video(); video.setPicUrl(picUrl); video.setVideoUrl(videoUrl); video.setText("传智播客是一个负责任的教育机构~"); //客户端未传递,手动模拟 video.setUserId(UserHolder.getUserId()); //4、调用api保存 videoApi.save(video); //5、构建返回值 return ResponseEntity.ok(null); }