SpringBoot+Redis+@Cacheable实现缓存功能

news2024/11/20 21:19:21

SpringBoot+Redis+@Cacheable实现缓存功能

  • 一、pom文件加入Redis与cache的依赖和yml配置
  • 二、EnableCaching允许使用注解进行缓存
  • 三、Redis配置
  • 四、业务逻辑
    • 1.UserController
    • 2.UserService
    • 3.UserServiceImpl
    • 4.AdminServiceImpl
    • 5.@Cacheable和@CachePut区别
  • 五、测试
    • 1.执行saveUser方法
    • 2.执行getUser方法
  • 六、项目目录及源码下载

一、pom文件加入Redis与cache的依赖和yml配置

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

        <!-- 如果需要集成redis,需要再加入redis包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- spring2.X集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!-- 数据库驱动 -->
        <!-- 如果是springboot项目可以不用自己指定版本号,spring boot给我们定义了 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
           <!-- <version>5.1.47</version> -->
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <!--spring-boot起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
server:
  port: 9003
spring:
  datasource:
    username: root
    password: root1234
    url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver
  redis:
    host: 127.0.0.1
    port: 6379
  cache:
    type: redis
    redis:
      # 缓存超时时间(毫秒ms)
      time-to-live: 60000
      # 是否缓存空值
      cache-null-values: true
# mybatis-plu配置
mybatis-plus:
  configuration:
    # map-underscore-to-camel-case为true可以将数据库的带下划线“”给去掉然后映射到实体类的属性上去。
    # sql执行的日志(包含sql操作的参数日志)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #指定xml映射位置
  mapper-locations: classpath*:mapper/*.xml
  #指是否对MyBatis xml 配置文件进行存在检查/默认值为false
  check-config-location: true
  #设置别名包扫描路径,通过该属性可以给包中的类注册别名,多个包以逗号分隔
  type-aliases-package: com.it.pojo

二、EnableCaching允许使用注解进行缓存

/**
 * 主启动类
 * EnableCaching允许使用注解进行缓存
 * @author shaohua
 */
@EnableCaching
@SpringBootApplication
@MapperScan(basePackages = "com.it.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

三、Redis配置

@Configuration
public class RedisConfig {
    /**
     * 注册自定义RedisCacheConfiguration组件,解决@Cacheable @Cacheput注解在向Redis中保存的Value是java序列化乱码的问题
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config.entryTtl(Duration.ofSeconds(30));
        config=config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
        return config;
    }
}

四、业务逻辑

1.UserController

/**
 * 用户控制层
 *
 * @author hua
 */
@RestController
@RequestMapping(value = "/user")
public class UserController {
    private final UserService userService;

    public UserController (UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/saveUser")
    public User saveUser(@RequestBody User user) {
        return userService.insert(user);
    }

    @GetMapping(value = "/{userId}")
    public ResponseEntity<User> getUser(@PathVariable Integer userId) {
        User user = userService.findUser(userId);
        HttpStatus status = user == null ? HttpStatus.NOT_FOUND: HttpStatus.OK;
        return new ResponseEntity<>(user, status);
    }

    @DeleteMapping(value = "/{userId}")
    public ResponseEntity deleteUser(@PathVariable Integer userId) {
        userService.delete(userId);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}

2.UserService

**
 * UserService
 *
 * @author hua
 */
public interface UserService extends IService<User>{

    /**
     * save use, put redis cache
     * @param user user data
     * @return saved user data
     */
     User insert(User user);

    /**
     * find user by id,redis cacheable
     * @param userId user id
     * @return if exist return the user, else return null
     */
     User findUser(Integer userId);

    /**
     * delete user by id, and remove redis cache
     * @param userId user id
     */
     void delete(Integer userId);
}

3.UserServiceImpl

/**
 * 实现类
 *
 * @author hua
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 存储数据库的同时,存储到缓存
     */
    @Override
    @CachePut(value = "user", key = "#result.id", unless = "#result eq null")
    public User insert(User user) {
        this.save(user);
        return user;
    }

    /**
     * 优先从redis缓存中读取(发现控制台并未执行sql)。如果缓存过期,则重新查数据,重新写入缓存
     */
    @Override
    @Cacheable(value = "user", key = "#userId", unless = "#result eq null")
    public User findUser(Integer userId) {
        User user = this.getById(userId);
        return user;
    }

    /**
     * 删除数据时同样清除缓存
     */
    @Override
    @CacheEvict(value = "user", key = "#userId")
    public void delete(Integer userId) {
        this.removeById(userId);
    }
}

4.AdminServiceImpl

/**
 * 用户实现类
 *
 * @author hua
 */
@Service
public class AdminServiceImpl implements AdminService {

    @Autowired
    UserService userService;

    /**
     * 保存String类型的数据,
     */
    @Override
    @CachePut(value = "张三")
    public void insert1() {
    }

    /**
     * 保存对象 形参方式保存
     */
    @Override
    @CachePut(value = "user", key = "#user.id", unless = "#user eq null")
    public void insert2(User user) {
    }
    // key = "#user.id": 取形参user中的id值, 作为key名称
    // value = "user": 形参user, 作为value值
    // unless = "#user eq null :判断 形参user对象是空的,若unless的结果为true,则(方法执行后的功能)不生效;若unless的结果为false,则(方法执行后的)功能生效。
    // 生效则会将形参user对象以json格式字符串保存到redis

    /**
     * 保存对象 返回值方式保存
     */
    @Override
    @CachePut(value = "user", key = "#result.id", unless = "#result eq null")
    public User insert3() {
        LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery();
        wrapper.eq(User::getName,"小花花");
        User user = userService.getOne(wrapper);
        return user;
    }
    // key = "#result.id": 取返回值user中的id值, 作为key名称
    // value = "user": 取返回值user, 作为value值
    // unless = "#result eq null: 判断 方法返回值是空的, 若unless的结果为true,则(方法执行后的功能)不生效;若unless的结果为false,则(方法执行后的)功能生效。
    // 生效则会将返回值user对象以json格式字符串保存到redis

    @Override
    @Cacheable(value = CacheTimes.D1, key = "#root.methodName", unless = "#result == null || #result.size() < 1", condition = "#skip != null")
    public List<String> getList(String skip) {
        return Arrays.stream(UUID.randomUUID().toString().split("-")).collect(Collectors.toList());
    }

    /*
        说明:@Cacheable注解有三个参数,value是必须的,还有key和condition。
        第一个参数value:指明了缓存将被存到什么地方.上面的代码保证findPersonByName的返回值Person对象将被存储在"employee"中。
        第二个参数key:指定key,任何存储在缓存中的数据为了高速访问都需要一个key。Spring默认使用被@Cacheable注解的方法的签名来作为key, 当然你可以重写key, 自定义key可以使用SpEL表达式。
        在findEmployee()的注解中"#name"是一个SpEL表达式,他将使用findPersonByName()方法中的name参数作为key。
        第三个参数condition(可选):
        功能1:缓存的条件, 决定是否缓存。@Cacheable的最后一个参数是condition(可选),同样的,也是引用一个SpEL表达式。但是这个参数将指明方法的返回结果是否被缓存。
        功能2:condition则判定了是否走换成逻辑。如果age<25,即condition是false,就不去读取缓存,而是直接执行当前目标方法,返回结果。
        上面的例子中,只有年龄小于25的时候才被缓存
     */
    @Cacheable(value = "employee", key = "#name" ,condition = "#age < 25")
    public User findEmployee(String name, int age) {
        return new User();
    }
}

5.@Cacheable和@CachePut区别

相同点
都是Spring的缓存注解。
不同点
@Cacheable:只会执行一次,当标记在一个方法上时表示该方法是支持缓存的,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果。
@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

五、测试

1.执行saveUser方法

利用@CachePut,存储数据库的同时存储到缓存
在这里插入图片描述
第一次和第二次执行saveUser方法,发现控制台打印了SQL语句,说明执行了SQL存储到MySQL数据了。
在这里插入图片描述
在这里插入图片描述
观察MySQL数据存储情况,存储了2条数据,主键id自增。
在这里插入图片描述
在这里插入图片描述
观察Redis数据存储情况,存储了2条数据,主键id自增。
在这里插入图片描述
在这里插入图片描述

2.执行getUser方法

利用@Cacheable,优先从redis缓存中读取(发现控制台并未执行sql)。如果缓存过期,则重新查数据,重新写入缓存。
发现执行多次,控制台均未打印SQL,说明均是从Redis读取数据(前提是Redis缓存未过期)。
在这里插入图片描述
在这里插入图片描述

六、项目目录及源码下载

在这里插入图片描述
源码下载,欢迎Star哦~ springboot-cacheable

参考资料
Spring 缓存的使用
SpringBoot通过@Cacheable注解实现缓存功能
Springboot+redis+@Cacheable实现缓存
SpringBoot2使用@Cacheable注解时,Redis中保存的Value为java序列化乱码问题的解决办法及源码分析
@Cacheable和@CachePut区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/159224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指offer----C语言版----第十七天----面试题23:链表中环的入口节点

目录 1. 链表中环的入口节点 1.1 环形链表Ⅰ 1.1.1 题目描述 1.1.2解题思路 1.1.3 扩展问题 1.2 环形链表Ⅱ 1.2.1 题目描述 1.2.2 思路分析 1. 链表中环的入口节点 在leetcode上的剑指offer专栏没有收录这道题目&#xff0c;但Leetcode上是有这道题目的&#xff0c;环…

U3D客户端框架之 音效管理器 与 Fmod介绍安装导入Unity

一、Fmod介绍与安装导入Unity 1.Fmod与Unity内置Audio播放器对比 Unity内置的Audio底层使用的是FMOD&#xff0c;但是功能不够齐全&#xff0c;高级一点的功能如混合(Mix)等无法使用&#xff1b; 音效管理应该和Unity工程解耦合&#xff0c;这样子可以减轻音效设计师的负担&a…

ArcGIS基础实验操作100例--实验86矢量面重叠分析

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验86 矢量面重叠分析 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…

初阶指针详解✍

目录1.内存和地址2.指针变量的大小3.指针类型的意义意义1&#xff1a;指针访问权限的大小意义2&#xff1a;指针类型决定指针的步长4.野指针野指针成因如何规避野指针5.指针的运算指针加减整数指针减指针指针的比较运算6.指针与数组的关系7.二级指针1.内存和地址 内存是电脑上特…

2、C语言程序规范

目录 1. 代码缩进 2. 变量、常量命名规范 3. 函数的命名规范 4. #include指令 5. 注释 6. main函数 7.函数返回值 8. 变量赋初值 俗话说&#xff0c;“没有规矩&#xff0c;不成方圆。” 如&#xff1a;第一个程序 #include <stdio.h>void main(){printf("…

基于java Springmvc+mybatis 电影院售票管理系统设计和实现以及文档

基于java Springmvcmybatis 电影院售票管理系统设计和实现以及文档 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留…

vue报错汇总

项目场景&#xff1a; 使用vue报错汇总。 1、项目启动不报错也不成功 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 项目启动时&#xff0c;一直启动不成功&#xff0c;末句提示 98% emitting Copyplugin… 原因分析&#xff1a; 最有可能是因为require或者import了…

系统设计技巧:使用Postgres作为发布/订阅和作业服务器

如果在项目中需要发布/订阅和作业服务器&#xff0c;可以尝试使用 Postgres。它将为您提供大量数据完整性和性能保证&#xff0c;并且不需要您或您的团队学习任何新技术。如果你正在做任何足够复杂的项目&#xff0c;你将需要一个 发布/订阅[1] 服务器来处理事件。本文将向你介…

黑马“兔年限定”春节礼盒准时送达,快来领!

哈咯艾瑞巴蒂&#xff0c;我是播妞前几天一个热搜引起了我的注意# 原来兔年要打384天的工 #看到这标题播妞突然头皮发紧我搜索了一下&#xff0c;原来是......农历癸卯兔年全年共有384天今年的春节是2023年1月22号2024年的春节是2月10号从今年春节到明年的春节算一年由于“闰二…

XCTF:ics-05

测试了所有功能点&#xff0c;大部分没有做出来&#xff0c;只有设备维护中心可以点击 查看源码&#xff0c;发现云平台设备维护中心这里有一个超链接 看到变量传参page&#xff0c;有点像页面文件包含功能&#xff0c;那有可能存在文件包含&#xff0c;测试下&#xff1a; …

ORB-SLAM2 --- LoopClosing::CorrectLoop函数

目录 1.函数作用 2.函数流程 3.code 4.函数解析 4.1 结束局部地图线程、全局BA&#xff0c;为闭环矫正做准备 4.2 根据共视关系更新当前关键帧与其它关键帧之间的连接关系 4.3 通过位姿传播&#xff0c;得到Sim3优化后&#xff0c;与当前帧相连的关键帧的位姿&#xf…

什么是计算机中的高速公路-总线?

文章目录总线是什么&#xff1f;常见总线类型有哪些&#xff1f;总线的串行和并行的区别&#xff1f;数据总线地址总线CPU的寻址能力32位CPU最大支持4G内存&#xff1f;控制总线总线的共享性和独自性系统总线的结构单总线结构双总线结构三总线结构总线传输的四个阶段总线仲裁集…

谷粒商城项目笔记之高级篇(二)

目录1.7 认证服务1.7.1 环境搭建1&#xff09;、创建认证服务微服务2&#xff09;、引入依赖3)、添加相应的域名4&#xff09;、动静分离5&#xff09;、nacos中注册6&#xff09;、配置网关7)、测试访问登录页面8&#xff09;、实现各个页面之间跳转1.7.2 验证码功能1)、验证码…

C++的类介绍(封装特性)

一、类的定义 1.1定义 类是c语言对编程思想的概括深化&#xff0c;其前期的C语言使能面向过程的语言&#xff0c;思想是注重对程序每一步的理解&#xff1b;而面向过程的是C语言之父把生活的类与对象的思想应用于程序设计之中&#xff0c;把程序抽象成一个个对象。 C面向对象…

将时间序列转换为指定的频率并指定填充方法来填充缺失值的DataFrame.asfreq()方法

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 时间序列的插值操作 提升序列中时间密度 DataFrame.asfreq() 选择题 关于以下python代码说法错误的一项是? import pandas as pd myIndexpd.date_range(1/12/2023,periods3,freqT) dfpd.D…

深入了解 LinkedBlockingQueue阻塞队列

1. 前言 今天来逐个方法解析下LinkedBlockingQueue. 其实跟上篇文件深入了解ArrayBlockingQueue 阻塞队列很类似。只不过底层实现不同。其实看名字就能看出来到底依据什么实现的。好了&#xff0c;废话不多说了&#xff0c;接下来让我们开始吧 至于API的使用情况跟ArrayBlockin…

JS垃圾回收

什么是GC GC就是Garbage Collection,程序工作过程中会产生很多垃圾&#xff0c;这些垃圾是程序不用的内存或者是之前用过了&#xff0c;以后不会再用的内存空间&#xff0c;而GC就是负责回收垃圾的。当然也不是所有的语言都会自带GC&#xff0c;比如Java、Python、Javascript等…

电脑修改用户(User)文件夹名称

情景&#xff1a;Windows 11 的用户名与 C 盘&#xff08;系统盘&#xff09;中的文件夹名称不对应&#xff08;可能是由于重装系统导致的&#xff09;&#xff0c;例如我笔记本中系统用户名是 “fly”&#xff0c;但文件夹名称却是“16490”。 Step 1&#xff1a;打开Administ…

智能图像处理:基于边缘去除和迭代式内容矫正的复杂文档图像校正

本文简要介绍ACM MM 2022录用论文“Marior: Margin Removal and Iterative Content Rectification for Document Dewarping in the Wild”的主要工作。该论文针对现有的矫正方法只能在紧密裁剪的文档图像上获得较为理想的矫正效果这一不足&#xff0c;提出了一个新的矫正方法Ma…

基于webrtc多人音视频的研究(一)

众所周知&#xff0c;WebRTC非常适合点对点&#xff08;即一对一&#xff09;的音视频会话。然而&#xff0c;当我们的客户要求超越一对一&#xff0c;即一对多、多对一设置多对多的解决方案或者服务&#xff0c;那么问题就来了&#xff1a;“我们应该采用什么样的架构&#xf…