SpringBoot详解整合Redis缓存方法


Posted in Java/Android onJuly 15, 2022

1、Spring Boot支持的缓存组件

在Spring Boot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.springframework.cache.CacheManager缓存管理器接口。

如果程序中没有定义类型为CacheManager的Bean组件或者是名为cacheResolver的CacheResolver缓存解析器,Spring Boot将尝试选择并启用以下缓存组件(按照指定的顺序):

(1)Generic

(2)JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)

(3)EhCache 2.x

(4)Hazelcast

(5)Infinispan

(6)Couchbase

(7)Redis

(8)Caffeine

(9)Simple

上面按照Spring Boot缓存组件的加载顺序,列举了支持的9种缓存组件,在项目中添加某个缓存管理组件(例如Redis)后,Spring Boot项目会选择并启用对应的缓存管理器。如果项目中同时添加了多个缓存组件,且没有指定缓存管理器或者缓存解析器(CacheManager或者cacheResolver),那么Spring Boot会按照上述顺序在添加的多个缓存中优先启用指定的缓存组件进行缓存管理。

刚刚讲解的Spring Boot默认缓存管理中,没有添加任何缓存管理组件能实现缓存管理。这是因为开启缓存管理后,Spring Boot会按照上述列表顺序查找有效的缓存组件进行缓存管理,如果没有任何缓存组件,会默认使用最后一个Simple缓存组件进行管理。Simple缓存组件是Spring Boot默认的缓存管理组件,它默认使用内存中的ConcurrentMap进行缓存存储,所以在没有添加任何第三方缓存组件的情况下,可以实现内存中的缓存管理,但是我们不推荐使用这种缓存管理方式

2、基于注解的Redis缓存实现

在Spring Boot默认缓存管理的基础上引入Redis缓存组件,使用基于注解的方式讲解Spring Boot整合Redis缓存的具体实现

(1)添加Spring Data Redis依赖启动器。在pom.xml文件中添加Spring Data Redis依赖启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

当我们添加进redis相关的启动器之后, SpringBoot会使用RedisCacheConfigratioin 当做生效的自动配置类进行缓存相关的自动装配,容器中使用的缓存管理器是RedisCacheManager , 这个缓存管理器创建的Cache为 RedisCache , 进而操控redis进行数据的缓存

(2)Redis服务连接配置

# Redis服务地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=

(3)对CommentService类中的方法进行修改使用@Cacheable、@CachePut、@CacheEvict三个注解定制缓存管理,分别进行缓存存储、缓存更新和缓存删除的演示

package com.lagou.service;
import com.lagou.pojo.Comment;
import com.lagou.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CommentService {
    @Autowired
    private CommentRepository commentRepository;
    /**
     * @Cacheable: 将该方法查询结果comment存放在SpringBoot默认缓存中
     * cacheNames: 起一个缓存的命名空间,对应缓存的唯一标识
     * value:缓存结果   key:默认只有一个参数的情况下,key值默认就是方法参数值; 如果没有参数或者多个参数的情况:会使用SimpleKeyGenerate来为生成key
     */
    @Cacheable(cacheNames = "comment", unless = "#result==null")
    public Comment findCommentById(Integer id) {
        Optional<Comment> byId = commentRepository.findById(id);
        if (byId.isPresent()) {
            Comment comment = byId.get();
            return comment;
        }
        return null;
    }
    // 更新方法
    @CachePut(cacheNames = "comment", key = "#result.id")
    public Comment updateComment(Comment comment) {
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        return comment;
    }
    // 删除方法
    @CacheEvict(cacheNames = "comment")
    public void deleteComment(Integer id) {
        commentRepository.deleteById(id);
    }
}

以上 使用@Cacheable、@CachePut、@CacheEvict注解在数据查询、更新和删除方法上进行了缓存管理。

其中,查询缓存@Cacheable注解中没有标记key值,将会使用默认参数值comment_id作为key进行数据保存,在进行缓存更新时必须使用同样的key;同时在查询缓存@Cacheable注解中,定义了“unless = "#result==null"”表示查询结果为空不进行缓存

控制层

package com.lagou.controller;
import com.lagou.pojo.Comment;
import com.lagou.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CommentController {
    @Autowired
    private CommentService commentService;
    @RequestMapping( "/findCommentById")
    public Comment findCommentById(Integer id) {
        Comment comment = commentService.findCommentById(id);
        return comment;
    }
    @RequestMapping(value = "/updateComment")
    public Comment updateComment(Comment comment) {
        Comment commentById = commentService.findCommentById(comment.getId());
        commentById.setAuthor(comment.getAuthor());
        return commentService.updateComment(commentById);
    }
    @RequestMapping(value = "/deleteComment")
    public void deleteComment(Integer id) {
        commentService.deleteComment(id);
    }
}

(4) 基于注解的Redis查询缓存测试

SpringBoot详解整合Redis缓存方法

可以看出,查询用户评论信息Comment时执行了相应的SQL语句,但是在进行缓存存储时出现了IllegalArgumentException非法参数异常,提示信息要求对应Comment实体类必须实现序列化(“DefaultSerializer requires a Serializable payload but received an object of type”)。

(5)将缓存对象实现序列化。

SpringBoot详解整合Redis缓存方法

(6)再次启动测试

访问“http://localhost:8080/findCommentById?id=1”查询id为1的用户评论信息,并重复刷新浏览器查询同一条数据信息,查询结果

SpringBoot详解整合Redis缓存方法

查看控制台打印的SQL查询语句

SpringBoot详解整合Redis缓存方法

还可以打开Redis客户端可视化管理工具Redis Desktop Manager连接本地启用的Redis服务,查看具体的数据缓存效果

SpringBoot详解整合Redis缓存方法

执行findById()方法查询出的用户评论信息Comment正确存储到了Redis缓存库中名为comment的名称空间下。其中缓存数据的唯一标识key值是以“名称空间comment::+参数值(comment::1)”的字符串形式体现的,而value值则是经过JDK默认序列格式化后的HEX格式存储。这种JDK默认序列格式化后的数据显然不方便缓存数据的可视化查看和管理,所以在实际开发中,通常会自定义数据的序列化格式

(7) 基于注解的Redis缓存更新测试。

先通过浏览器访问“http://localhost:8080/updateComment/1/shitou”更新id为1的评论作者名为shitou;

接着,继续访问“http://localhost:8080/findCommentById?id=1”查询id为1的用户评论信息

SpringBoot详解整合Redis缓存方法

可以看出,执行updateComment()方法更新id为1的数据时执行了一条更新SQL语句,后续调用findById()方法查询id为1的用户评论信息时没有执行查询SQL语句,且浏览器正确返回了更新后的结果,表明@CachePut缓存更新配置成功

(8)基于注解的Redis缓存删除测试

通过浏览器访问“http://localhost:8080/deleteComment?id=1”删除id为1的用户评论信息;

SpringBoot详解整合Redis缓存方法

执行deleteComment()方法删除id为1的数据后查询结果为空,之前存储在Redis数据库的comment相关数据也被删除,表明@CacheEvict缓存删除成功实现

通过上面的案例可以看出,使用基于注解的Redis缓存实现只需要添加Redis依赖并使用几个注解可以实现对数据的缓存管理。另外,还可以在Spring Boot全局配置文件中配置Redis有效期,示例代码如下:

# 对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒
spring.cache.redis.time-to-live=60000

上述代码中,在Spring Boot全局配置文件中添加了“spring.cache.redis.time-to-live”属性统一配置Redis数据的有效期(单位为毫秒),但这种方式相对来说不够灵活

3、基于API的Redis缓存实现

在Spring Boot整合Redis缓存实现中,除了基于注解形式的Redis缓存实现外,还有一种开发中常用的方式——基于API的Redis缓存实现。这种基于API的Redis缓存实现,需要在某种业务需求下通过Redis提供的API调用相关方法实现数据缓存管理;同时,这种方法还可以手动管理缓存的有效期。

下面,通过Redis API的方式讲解Spring Boot整合Redis缓存的具体实现

(1)使用Redis API进行业务数据缓存管理。在com.lagou.service包下编写一个进行业务处理的类ApiCommentService

package com.lagou.service;
import com.lagou.pojo.Comment;
import com.lagou.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
public class ApiCommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    // 使用API方式进行缓存,先去缓存中查找,缓存中有,直接返回;没有查询数据库
    public Comment findCommentById(Integer id) {
        Object o = redisTemplate.opsForValue().get("comment_" + id);
        if (o != null) {
            return (Comment) o; // 查询到数据
        } else {
            // 缓存中没有,从数据库中查询
            Optional<Comment> byId = commentRepository.findById(id);
            if (byId.isPresent()) {
                Comment comment = byId.get();
                // 将查询结果存入到缓存中,同时还可以设置有效期
                redisTemplate.opsForValue().set("comment_" + id, comment, 1, TimeUnit.DAYS); // 过期时间为一天
                return comment;
            }
        }
        return null;
    }
    // 更新方法
    public Comment updateComment(Comment comment) {
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        // 将更新数据进行缓存更新
        redisTemplate.opsForValue().set("comment_" + comment.getId(), comment, 1, TimeUnit.DAYS);
        return comment;
    }
    // 删除方法
    public void deleteComment(Integer id) {
        commentRepository.deleteById(id);
        redisTemplate.delete("comment_" + id);
    }
}

(2)编写Web访问层Controller文件

package com.lagou.controller;
import com.lagou.pojo.Comment;
import com.lagou.service.ApiCommentService;
import com.lagou.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiCommentController {
    @Autowired
    private ApiCommentService commentService;
    @RequestMapping( "/findCommentById")
    public Comment findCommentById(Integer id) {
        Comment comment = commentService.findCommentById(id);
        return comment;
    }
    @RequestMapping(value = "/updateComment")
    public Comment updateComment(Comment comment) {
        Comment commentById = commentService.findCommentById(comment.getId());
        commentById.setAuthor(comment.getAuthor());
        return commentService.updateComment(commentById);
    }
    @RequestMapping(value = "/deleteComment")
    public void deleteComment(Integer id) {
        commentService.deleteComment(id);
    }
}

基于API的Redis缓存实现的相关配置。基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持,所以这里可以选择将添加在项目启动类上的@EnableCaching进行删除或者注释

到此这篇关于SpringBoot详解整合Redis缓存方法的文章就介绍到这了,更多相关SpringBoot Redis缓存内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
IDEA使用SpringAssistant插件创建SpringCloud项目
Jun 23 Java/Android
看完这篇文章获得一些java if优化技巧
Jul 15 Java/Android
在Spring-Boot中如何使用@Value注解注入集合类
Aug 02 Java/Android
logback 实现给变量指定默认值
Aug 30 Java/Android
SpringBoot2零基础到精通之数据库专项精讲
Mar 22 Java/Android
Java中API的使用方法详情
Apr 06 Java/Android
Android使用EventBus发送消息,Fragment中接收消息的方法会执行多次
Apr 24 Java/Android
详解Android中的TimePickerView(时间选择器)的用法
Apr 30 Java/Android
Android开发手册TextInputLayout样式使用示例
Jun 10 Java/Android
Springboot中如何自动转JSON输出
Jun 16 Java/Android
springboot集成redis存对象乱码的问题及解决
Jun 16 Java/Android
Java实现贪吃蛇游戏的示例代码
Sep 23 Java/Android
maven 解包依赖项中的文件的解决方法
Jul 15 #Java/Android
SpringBoot详解自定义Stater的应用
Jul 15 #Java/Android
MyBatis XPathParser解析器使用范例详解
Jul 15 #Java/Android
SpringBoot接入钉钉自定义机器人预警通知
Jul 15 #Java/Android
SpringBoot深入分析讲解监听器模式下
Jul 15 #Java/Android
tree shaking对打包体积优化及作用
Jul 07 #Java/Android
MyBatis在注解上使用动态SQL方式(@select使用if)
Jul 07 #Java/Android
You might like
用文本文件制作留言板提示(上)
2006/10/09 PHP
php字符串分割函数用法实例
2015/03/17 PHP
TP5框架实现一次选择多张图片并预览的方法示例
2020/04/04 PHP
PHP开发API接口签名生成及验证操作示例
2020/05/27 PHP
jquery 子窗口操作父窗口的代码
2009/09/21 Javascript
JS.GetAllChild(element,deep,condition)使用介绍
2013/09/21 Javascript
Document:getElementsByName()使用方法及示例
2013/10/28 Javascript
优化Node.js Web应用运行速度的10个技巧
2014/09/03 Javascript
js实现仿百度汽车频道选择汽车图片展示实例
2015/05/06 Javascript
javascript实现3D变换的立体圆圈实例
2015/08/06 Javascript
每个程序员都需要学习 JavaScript 的7个理由小结
2016/09/03 Javascript
通过Ajax使用FormData对象无刷新上传文件方法
2016/12/08 Javascript
Vue.js仿微信聊天窗口展示组件功能
2017/08/11 Javascript
vue 使用vue-i18n做全局中英文切换的方法
2018/10/29 Javascript
利用jsonp解决js读取本地json跨域的问题
2018/12/11 Javascript
JS立即执行函数功能与用法分析
2019/01/15 Javascript
angular4+百分比进度显示插件用法示例
2019/05/05 Javascript
JS实现分页导航效果
2020/02/19 Javascript
在pycharm中开发vue的方法步骤
2020/03/04 Javascript
实用的 vue tags 创建缓存导航的过程实现
2020/12/03 Vue.js
Python简单实现socket信息发送与监听功能示例
2018/01/03 Python
Java与Python两大幸存者谁更胜一筹呢
2018/04/12 Python
python可视化实现代码
2019/01/15 Python
Python 操作 ElasticSearch的完整代码
2019/08/04 Python
Python视频编辑库MoviePy的使用
2020/04/01 Python
使用CSS3编写灰阶滤镜来制作黑白照片效果的方法
2016/05/09 HTML / CSS
The Hut美国/加拿大:英国领先的豪华在线百货商店
2019/03/26 全球购物
Ootori在线按摩椅店:一家专业的按摩椅制造商
2019/04/10 全球购物
大学生农村教师实习自我鉴定
2013/09/21 职场文书
考博专家推荐信
2014/05/10 职场文书
高校群众路线教育实践活动剖析材料
2014/10/10 职场文书
个人贷款收入证明
2014/10/26 职场文书
2014年政府采购工作总结
2014/12/09 职场文书
如何利用Matlab制作一款真正的拼图小游戏
2021/05/11 Python
python自动化操作之动态验证码、滑动验证码的降噪和识别
2021/08/30 Python
动画《平凡职业成就世界最强》宣布制作OVA
2022/04/01 日漫